diff --git a/README.md b/README.md index 9899f2c59..5a4fc43ef 100644 --- a/README.md +++ b/README.md @@ -422,6 +422,166 @@ Parameters for banner shortcode:
+#### đŸ§© Congratulations-banner + +![](readme-assets/shortcode-congratulations-banner.png) + +Example: +``` +{{< congratulations-banner +title="Congratulations!" +description="You’ve reached the end of Day 0!" >}} +``` + +Parameters for congratulations-banner shortcode: +- `title` - required +- `description` - required +- `image` - optional, default "/img/congratulations.svg" + +
+ +#### đŸ§© List + +- List - variant 1 + +![](readme-assets/shortcode-list-1.png) + +Example: +``` +{{< list isCompleted="false" >}} +- Set up your Qdrant Cloud account +- Created your first Qdrant collection +- Inserted vectors with metadata +- Performed a similarity search +{{< /list >}} +``` + +- List - variant 2 + +![](readme-assets/shortcode-list-2.png) + +Example: +``` +{{< list isCompleted="true" >}} +- Set up your Qdrant Cloud account +- Created your first Qdrant collection +- Inserted vectors with metadata +- Performed a similarity search +{{< /list >}} +``` + +Parameters for list shortcode: +- `isCompleted` - optional, default "false" + +
+ +#### đŸ§© Course card + +- Course-card - variant 1 + +![](readme-assets/shortcode-course-card-1.png) + +Example: +``` +{{< course-card +title="Skills you’ll gain:" +image="/icons/outline/training-white.svg" +isWideList="true">}} +- Vector search fundamentals +- Performance optimization +- Hybrid and similarity search +- Portfolio project development +{{< /course-card >}} +``` + +- Course-card - variant 2 + +![](readme-assets/shortcode-course-card-2.png) + +Example: +``` +{{< course-card +title="Why Start Today" +image="/icons/outline/rocket-white-light.svg" +link="/course/day-0/">}} +- Seeing practical examples (e.g., hybrid search, sparse+dense vectors) +- Learning key deployment tactics (multi-node clusters, on-disk indexing, RBAC) +- Building a final portfolio-grade project to showcase +{{< /course-card >}} +``` + +Parameters for course card shortcode: +- `title` - required +- `isWideList` - optional, default "false" +- `image` - optional, default "/icons/outline/training-white.svg" +- `link` - optional, default null + +
+ +#### đŸ§© Date + +![](readme-assets/shortcode-date.png) + +Example: +``` +{{< date >}} Day 1 {{< /date >}} +``` + +Parameters for date shortcode: +- `image` - optional, default "/icons/outline/date-blue.svg" + +
+ +#### đŸ§© Cards list + +![](readme-assets/shortcode-cards-list.png) + +Example: +``` +{{< cards-list >}} +- icon: /courses/course-integrations/quotient.svg + title: Quotient + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/superlinked.svg + title: Superlinked + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/twelveLabs.svg + title: TwelveLabs + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/aparavi.svg + title: APARAVI + content: Qdrant is compatible with Cohere co.embed API. +{{< /cards-list >}} +``` + +
+ +#### đŸ§© Accordion + +![](readme-assets/shortcode-accordion.png) + +Example: +``` +{{< accordion >}} +- title: "Days 0: Setup, Orientation & “Hello Qdrant!”" + content: | + - Welcome & Course Orientation + - Environment Setup + - Mini “Hello Qdrant!” Demo + +- title: "Day 1: Core Qdrant Data Model & Vector Search 101" + content: Content + +- title: "Days 2: Indexing & Vector Storage Architecture" + content: Content +{{< /accordion >}} +``` + +
+ #### đŸ§© Code Snippets Widget ![](readme-assets/shortcode-snippets.png) @@ -509,4 +669,4 @@ When use `seo_schema` and `seo_schema_json` together, `seo_schema` will be used Use `seo_schema_json` if you want to reuse the same schema for multiple pages to avoid duplication and make it easier to maintain. -[To Index](#index) \ No newline at end of file +[To Index](#index) diff --git a/qdrant-landing/content/articles/muvera-embeddings.md b/qdrant-landing/content/articles/muvera-embeddings.md new file mode 100644 index 000000000..f26839c9b --- /dev/null +++ b/qdrant-landing/content/articles/muvera-embeddings.md @@ -0,0 +1,206 @@ +--- +title: "MUVERA: Making Multivectors More Performant" +short_description: "Making multi-vector retrieval more efficient by approximating it with single-vector search" +description: "Multi-vector representations are superior to single-vector embeddings in many benchmarks. MUVERA embeddings aim to solve the problem of slow multi-vector search by creating a single-vector representation that approximates the multi-vector representation. This single vector can be used for fast initial retrieval using traditional vector search methods, and then the multi-vector representation can be used for reranking the top results." +preview_dir: /articles_data/muvera-embeddings/preview +social_preview_image: /articles_data/muvera-embeddings/preview/social_preview.jpg +author: Kacper Ɓukawski +author_link: https://kacperlukawski.com +date: 2025-09-05T00:00:00.000Z +category: vector-search-manuals +--- + +## What are MUVERA Embeddings? + +Multi-vector representations are superior to single-vector embeddings in many benchmarks. It might be tempting to use +them right away, but there is a catch: they are slower to search. Traditional vector search structures like +[HNSW](/documentation/concepts/indexing/#vector-index) are optimized for retrieving the nearest neighbors of a single +query vector using simple metrics such as cosine similarity. These indexes are not suitable for multi-vector retrieval +strategies, such as MaxSim, where a query and document are each represented by multiple vectors and the final score is +computed as the maximum similarity over all cross-pairings. MaxSim is inherently asymmetric and non-metric, so HNSW +could potentially help us find the closest document token to a given query token, but that does not mean the whole +document is the best hit for the query. + +[MUVERA embeddings](https://research.google/blog/muvera-making-multi-vector-retrieval-as-fast-as-single-vector-search/), +introduced by Google Research, aim to solve this problem. The idea is to create a single vector that approximates the +multi-vector representation. This single vector can be used for fast initial retrieval using traditional vector search +methods, and then the multi-vector representation can be used for reranking the top results. This approach combines the +speed of single-vector search with the accuracy of multi-vector retrieval. Reranking with multi-vector representations +is much faster when applied to a small set of candidates rather than the entire dataset. + +![High-level idea of MUVERA embeddings](/articles_data/muvera-embeddings/muvera-high-level.png) + +[FastEmbed 0.7.2 introduces support for MUVERA embeddings](#muvera-in-fastembed), and this article explains the concept +in detail. + +## How MUVERA Embeddings are created + +**MUVERA** (**Mu**lti-**Ve**ctor **R**etrieval **A**lgorithm) transforms variable-length sequences of vectors into +fixed-dimensional representations. Since the number of vectors in the input varies between documents and queries, and +might be even different between different documents, we need a universal way to represent them in a fixed-size format. +Clustering the vector space of the tokens and projecting the individual multi-vector representations to +a lower-dimensional space using the created clustering helps achieve this. The authors of the MUVERA paper suggest using +the Locality-Sensitive Hashing (LSH) technique called SimHash. + +### Glossary of parameters + +There are a few parameters that control the MUVERA embedding creation process, so in order to avoid confusion, here is +a quick definition of them: + +- `dim`: Dimensionality of a single token vector in the original multi-vector representation, depends on the model used +- `k_sim`: Number of random hyperplanes used in SimHash, which determines the number of clusters as `2^k_sim` +- `dim_proj`: Dimensionality of the projected vectors after random projection +- `r_reps`: Number of independent repetitions of the SimHash projection and random projection process + +### SimHash projection for clustering + +SimHash is one of the LSH techniques that uses random hyperplanes to hash input vectors into binary codes. In the first +step, this method chooses `k_sim` random hyperplanes (normal vectors) from a standard normal distribution. These +hyperplanes divide the vector space into `2^k_sim` regions, because each vector can be on either side of each +hyperplane. Here is how such a space division could look like for `k_sim=3`: + +![SimHash space partitioning](/articles_data/muvera-embeddings/simhash-space-partitioning.png) + +Each token vector is now assigned to one of these regions based on which side of each hyperplane it falls on. This is +done by computing the dot product of the input vector with each hyperplane normal vector, and taking the sign of each +result. Since each of our regions can be represented as a binary string of length `k_sim` (where each bit indicates +which side of a hyperplane the vector is on), we can interpret this binary string as an integer to get a cluster ID. + +![SimHash cluster assignement](/articles_data/muvera-embeddings/simhash-cluster-assignment.png) + +### Fixed Dimensional Encoding (FDE) creation + +Once all input vectors are assigned to clusters, we can aggregate the vectors belonging to each of the clusters. This +process is slightly different for documents and queries. In both cases, we'll end up with a fixed-dimensional vector +with the same number of dimensions, but the way we compute these vectors differs. + +#### Document clustering + +Once we have assigned all the document token vectors to clusters, we compute the cluster centroids by averaging all +vectors in each cluster. This gives us a representative vector for each cluster. If a cluster ends up being empty (i.e., +no vectors were assigned to it), we fill it using vector from the nearest non-empty cluster. The distance between +clusters is determined by the Hamming distance between their cluster IDs. + +![FDE document processing](/articles_data/muvera-embeddings/fde-document-processing.png) + +As a result we get `2^k_sim` vectors, each of them being `dim`-dimensional. + +#### Query clustering + +Query processing is slightly different. Instead of computing the average vector for each cluster, we compute the sum of +all vectors in each cluster. This means that the resulting vectors will have larger magnitudes for clusters with more +assigned vectors. The idea is that it preserves the natural distribution of query terms, which can be beneficial for +retrieval. Unlike with documents, we do not fill empty clusters for queries. Queries are typically shorter than +documents, so it's more likely that some clusters will be empty. Filling them could introduce noise and distort the +representation, as each term would contribute to the dot product multiple times. + +![FDE query processing](/articles_data/muvera-embeddings/fde-query-processing.png) + +Again, we end up with `2^k_sim` vectors of size `dim`. Because we do not fill empty clusters, some of these vectors +might be zero vectors. + +### Dimensionality reduction through Random Projection + +The paper reports results from experiments with `k_sim` values of 4 and 5. Practically, this means 16 or 32 clusters, +respectively. If the original token vectors have a dimensionality of 128, the resulting FDE would have a dimensionality +of `16 * 128 = 2048` or `32 * 128 = 4096`. However, a single SimHash projection with just 4 or 5 hyperplanes might not +capture enough information about the input vectors. To improve the quality of the FDEs, the authors suggest repeating +the process multiple times with independent SimHash projections and concatenating the results. Practically, with +`r_reps=20` repetitions, we would end up with FDEs of size `20 * 16 * 128 = 40960` or `20 * 32 * 128 = 81920`, which is +quite large and could slow down single-vector search significantly. For that reason, the authors suggest applying random +projection to reduce the dimensionality of each FDE. This involves multiplying each cluster vector by a random +matrix with entries from `{-1, +1}` and applying a scaling factor of `1/√(dim_proj)`. This matrix has a shape of +`(dim, dim_proj)`, where `dim_proj` is the desired dimensionality of the projected vectors. The resulting FDE will then +have a size of `r_reps * 2^k_sim * dim_proj`. + +![Random projection](/articles_data/muvera-embeddings/random-projection.png) + +The results from all repetitions are then concatenated to form the final FDE. This repetition helps to create a more +robust representation that better approximates the original multi-vector embedding. + +### Final random projection + +The original paper suggests applying an additional random projection to the final FDE to further reduce its +dimensionality. It is an optional step that can be used if the resulting FDE is still too large. This final projection +uses another random matrix with entries from `{-1, +1}` to project the FDE to a desired lower dimensionality. + +## Practical considerations + +MUVERA embeddings seem to be a promising approach to make multi-vector retrieval more efficient. The paper **recommends +using it as an initial retrieval step followed by reranking with the original multi-vector representation**. This +approach requires storing both the MUVERA embeddings and the original multi-vector representations, which increases +storage requirements. + +Please note that the MUVERA embeddings might be way larger than the single dense vectors produced by traditional +embedding models you might be used to. For example, using `k_sim=6` (64 clusters), `dim_proj=32`, and `r_reps=20` with +128-dimensional token vectors results in FDEs of size `20 * 64 * 32 = 40960`. This is significantly larger than typical +single-vector embeddings, which are around a few thousand dimensions at most. The increased size of MUVERA embeddings +can impact storage and retrieval efficiency, so it's important to consider these factors when deciding to use them. + +### Impact on search performance + +To evaluate the effectiveness of MUVERA embeddings, we benchmarked three different approaches on the BeIR nfcorpus +dataset using the ColBERTv2 model. The MUVERA configuration used the following parameters: `k_sim=5`, `dim_proj=16`, +and `r_reps=20`: + +| Approach | NDCG@1 | NDCG@5 | NDCG@10 | +|-----------------------------|--------|--------|---------| +| Full multi-vector (ColBERT) | 0.478 | 0.387 | 0.347 | +| MUVERA-only | 0.319 | 0.267 | 0.242 | +| MUVERA + reranking | 0.475 | 0.383 | 0.343 | + +The results show that MUVERA-only search trades some accuracy for speed, achieving about 70% of the full multi-vector +performance. However, using MUVERA for initial retrieval followed by multi-vector reranking recovers nearly all the +original performance while maintaining the efficiency benefits for the initial search phase. + +It's becoming especially interesting when you consider the search latency improvements. In our benchmarks, we observed +significant speed gains: + +| Approach | Average Search Time (seconds) | +|-----------------------------|-------------------------------| +| Full multi-vector (ColBERT) | 1.27 | +| MUVERA-only | 0.15 | +| MUVERA + reranking | 0.18 | + +MUVERA-only search is approximately 8x faster than full multi-vector search, while the hybrid approach with reranking +still achieves about 7x speed improvement while maintaining nearly identical search quality. + +## MUVERA in FastEmbed + +[FastEmbed](/documentation/fastembed/) provides late interaction text (ColBERT) and multimodal (ColPali) embeddings. +Version 0.7.2 has introduced support for MUVERA embeddings which is compatible with any multi-vector representation, +and available as a post-processing step. + + + +```python +import numpy as np +from fastembed import LateInteractionTextEmbedding +from fastembed.postprocess import Muvera + +# Create a multi-vector model (ColBERT in this case) and then wrap it with MUVERA +model = LateInteractionTextEmbedding(model_name="colbert-ir/colbertv2.0") +muvera = Muvera.from_multivector_model( + model=model, + k_sim=6, + dim_proj=32, + r_reps=20 +) + +# Create embeddings of the sample text and then process them with MUVERA +embeddings = np.array(list(model.embed(["sample text"]))) +fde = muvera.process_document(embeddings[0]) +``` + +## Try it out today + +If you're already using multi-vector retrieval, upgrading to FastEmbed 0.7.2+ will unlock MUVERA's 7x speed improvements +while maintaining nearly identical search quality. And if you've always wanted to experiment with multi-vector retrieval +but were held back by performance concerns or complexity, now is the perfect time to start. MUVERA removes those +traditional barriers, making advanced retrieval techniques accessible and practical for production use. Simply upgrade +your FastEmbed installation with `pip install --upgrade fastembed` and start benefiting from the power of multi-vector +search without the traditional speed penalties. + diff --git a/qdrant-landing/content/articles/sparse-vectors.md b/qdrant-landing/content/articles/sparse-vectors.md index a1143048f..11476377a 100644 --- a/qdrant-landing/content/articles/sparse-vectors.md +++ b/qdrant-landing/content/articles/sparse-vectors.md @@ -26,7 +26,7 @@ Sparse vectors are like the Marie Kondo of data—keeping only what sparks joy ( Consider a simplified example of 2 documents, each with 200 words. A dense vector would have several hundred non-zero values, whereas a sparse vector could have, much fewer, say only 20 non-zero values. -In this example: We assume it selects only 2 words or tokens from each document. The rest of the values are zero. This is why it's called a sparse vector. +In this example: We assume it selects only 2 words or tokens from each document. ```python dense = [0.2, 0.3, 0.5, 0.7, ...] # several hundred floats diff --git a/qdrant-landing/content/blog/case-study-alhena.md b/qdrant-landing/content/blog/case-study-alhena.md index 67182f05d..b2bea3d9d 100644 --- a/qdrant-landing/content/blog/case-study-alhena.md +++ b/qdrant-landing/content/blog/case-study-alhena.md @@ -60,9 +60,9 @@ With Qdrant handling retrieval, Alhena no longer needed to customize infrastruct ## Hitting production-grade performance targets -Latency was a critical metric for Alhena. With FAISS, vector search on catalogs with 100,000+ items often took three seconds or more. That delayed the start of agent response streaming, making the AI feel sluggish and hurting the user experience. Pinecone helped on large indexes, but introduced latency on small ones, and couldn’t handle hybrid filtering needs. +Latency was a critical metric for Alhena. With FAISS, vector search on catalogs with 100,000+ items went far above their latency budget. That delayed the start of agent response streaming, making the AI feel sluggish and hurting the user experience. Pinecone helped on large indexes, but introduced latency on small ones, and couldn’t handle hybrid filtering needs. -Qdrant reduced retrieval latency on the same datasets to approximately 300 milliseconds. That enabled Alhena to meet its internal P95 SLA of 3.5 seconds from query to first token, even after accounting for hallucination detection, policy enforcement, and contextual rewriting. +Qdrant reduced retrieval latency by up to 90% on the same datasets. That enabled Alhena to meet its internal P95 SLA from query to first token, even after accounting for hallucination detection, policy enforcement, and contextual rewriting. *“We track every millisecond. Qdrant helped us cut vector retrieval time by 90 percent at scale. That’s what made it possible to stay under our latency SLA.”* — Kang-Chi Ho diff --git a/qdrant-landing/content/blog/case-study-fieldy.md b/qdrant-landing/content/blog/case-study-fieldy.md new file mode 100644 index 000000000..1aaa2fdc8 --- /dev/null +++ b/qdrant-landing/content/blog/case-study-fieldy.md @@ -0,0 +1,66 @@ +--- +draft: false +title: "How Fieldy AI Achieved Reliable AI Memory with Qdrant" +short_description: "Fieldy AI migrated to Qdrant to deliver fault-tolerant, real-time memory recall while reducing infrastructure costs by two-thirds." +description: "Discover how Fieldy AI built a fault-tolerant AI memory platform with Qdrant, achieving 100% reliable real-time recall, seamless hybrid search, and significant cost savings at scale." +preview_image: /blog/case-study-fieldy/social_preview_partnership-fieldy.jpg +social_preview_image: /blog/case-study-fieldy/social_preview_partnership-fieldy.jpg +date: 2025-09-04 +author: "Daniel Azoulai" +featured: false + +tags: +- Fieldy +- vector search +- hybrid search +- AI memory +- cost reduction +- reliability +- case study +--- + +## Fieldy AI’s migration to Qdrant: Building a fault-tolerant AI memory platform + +![How Fieldy AI Achieved Reliable AI Memory with Qdrant](/blog/case-study-fieldy/case-study-fieldy-bento-dark.jpg) + +### Capturing and retrieving a lifetime of conversations + +Fieldy is a hands-free wearable AI note taker that continuously records, transcribes, and organizes real-world conversations into your personal, searchable memory. The system’s goal is simple in concept but demanding in execution: capture every relevant spoken interaction, transcribe it with high accuracy, and make it instantly retrievable. This requires a robust ingestion pipeline, a scalable [vector search](https://qdrant.tech/documentation/overview/) layer, and a retrieval process capable of handling growing volumes of multimodal data without introducing latency or errors. + +From the start, the engineering team treated transcription reliability as the primary design constraint. If a conversation is not captured in the moment, it cannot be reconstructed later. This applies equally to Bluetooth transfer from the AI wearable pendant to the app, HTTPS uploads to the backend, speech-to-text transcription, embedding generation, and [vector database ingestion](https://qdrant.tech/documentation/database-tutorials/bulk-upload/). Every component had to meet this standard. + +### How Fieldy AI differentiates in a crowded market + +Fieldy’s multilingual, real-time transcription and instant recall make it a trusted voice recorder for professionals, healthcare providers, and anyone needing memory support, including those with ADHD. The engineering team is built to iterate quickly on quality and features in this fast-moving space. They maintain a direct feedback loop with users, enabling rapid prioritization of features that matter most in real usage. Finally, Fieldy’s multilingual transcription enables high-accuracy transcription in over 100 languages, using custom speech-to-text pipelines and continuous evaluation. + +![Product screenshot](/blog/case-study-fieldy/fieldy-device-image.jpg) + +*Fieldy's device* + +### Reliability challenges with the initial vector database + +Fieldy’s original architecture used Weaviate for vector storage and retrieval. Both the hosted and self-hosted deployments experienced persistent operational issues, most notably 5xx errors affecting roughly 10% of requests. These failures occurred both during ingestion and search, undermining the product’s promise of complete and accessible memory. Attempts to address the problem, including migrating to self-hosted infrastructure, provided only temporary relief before the errors returned months later. + +For the engineering team, these failures had two serious implications. First, missing data meant a permanent loss of user trust. Second, time spent debugging database issues directly slowed feature development. The team’s requirements for a replacement were clear: the new [vector database](https://qdrant.tech/documentation/overview/) had to eliminate persistent query failures, ingest and search tens of millions of vectors with low latency, and run with minimal operational intervention. + +### Selecting and deploying Qdrant + +After evaluating alternatives, Fieldy selected [Qdrant](http://qdrant.tech) for its stability, straightforward configuration, and suitability for self-hosted deployment. They opted to run Qdrant in the same environment as their backend services, ensuring low-latency access and avoiding the cross-region connectivity issues that had contributed to failures in the previous architecture. + +The migration process took less than a week. The team reused their existing vector schema to avoid redesigning their indexing logic during the transition. Embeddings, generated using Cohere’s multilingual v3 model, were batched locally before ingestion, replacing the integrated embedding calls previously handled within Weaviate. Following [Qdrant best practices](https://qdrant.tech/documentation/guides/optimize/), they disabled indexing during bulk import to maximize throughput, re-enabling it only after the migration was complete. The backend API calls were updated to use Qdrant’s gRPC interface, and [hybrid search](https://qdrant.tech/articles/hybrid-search/) with BM25 was combined with dense vector retrieval via [Reciprocal Rank Fusion (RRF)](https://qdrant.tech/documentation/concepts/hybrid-queries/#hybrid-search) for relevance scoring. + +### Architecture after migration + +In Fieldy’s current architecture, the AI transcription device streams audio to the mobile app over Bluetooth. The app sends audio to the backend via HTTPS, where it is processed by a speech-to-text model to produce a transcript. This transcript and embeddings are stored in Qdrant. + +When a user submits a query in the chat interface, the backend’s retrieval agent performs a [hybrid search](https://qdrant.tech/articles/hybrid-search/) in Qdrant. The HNSW index is used for dense vector similarity, BM25 handles term matching, and results are merged with Reciprocal Rank Fusion. Conversation metadata is fetched from Firestore for context assembly before the results are returned to the user. + +### Results in production + +Since the migration, Fieldy has delivered 100% reliable real-time AI recall and seamless memory search, ensuring the AI note taker’s performance matches its promise. Hosting Qdrant alongside the backend has reduced latency and virtually eliminated the network errors previously seen in cross-region deployments. + +Fieldy also achieved a two-thirds reduction in infrastructure costs after moving to Qdrant, while scaling to handle tens of millions of embeddings without operational incidents. Post-migration interventions have been limited to planned storage increases. + +### Next steps for retrieval quality + +With reliability and cost efficiency achieved, Fieldy’s engineering focus is shifting toward retrieval quality. Planned improvements include adding [location filtering](https://qdrant.tech/documentation/concepts/filtering/#geo) and [datetime filtering](https://qdrant.tech/documentation/concepts/filtering/#datetime-range) within Qdrant to refine result sets, experimenting with late chunking strategies, and testing parallel hybrid searches to increase recall on complex multi-faceted queries. The team is also exploring embedding summaries alongside raw transcript segments to improve retrieval performance on high-level or thematic searches. \ No newline at end of file diff --git a/qdrant-landing/content/blog/case-study-gooddata.md b/qdrant-landing/content/blog/case-study-gooddata.md index b7a37f69d..101c259cf 100644 --- a/qdrant-landing/content/blog/case-study-gooddata.md +++ b/qdrant-landing/content/blog/case-study-gooddata.md @@ -41,7 +41,7 @@ GoodData transitioned to a Retrieval-Augmented Generation (RAG) strategy, requir GoodData leveraged Qdrant’s official Helm chart, deploying smoothly in Kubernetes and efficiently managing near-real-time embedding updates, crucial for multilingual semantic layers. -![Old Approach](/blog/case-study-gooddata/gooddata-diagram-1.png) +![Old Approach](/blog/case-study-gooddata/gooddata-diagram-1.1.png) ### Real-Time Performance and Scalability Gains diff --git a/qdrant-landing/content/blog/case-study-opentable.md b/qdrant-landing/content/blog/case-study-opentable.md new file mode 100644 index 000000000..0d12a67e9 --- /dev/null +++ b/qdrant-landing/content/blog/case-study-opentable.md @@ -0,0 +1,66 @@ +--- +draft: false +title: "How OpenTable Reinvented Restaurant Discovery with Qdrant" +short_description: "OpenTable transformed restaurant search with Concierge, an AI-powered assistant built on Qdrant." +description: "Discover how OpenTable built Concierge, a generative AI dining assistant powered by Qdrant, achieving accurate retrieval, global scalability, and operational stability while redefining how diners discover restaurants." +preview_image: /blog/case-study-opentable/social-preview-opentable.jpg +social_preview_image: /blog/case-study-opentable/social-preview-opentable.jpg +date: 2025-09-02 +author: "Daniel Azoulai" +featured: true + +tags: +- OpenTable +- vector search +- generative AI +- restaurant discovery +- sparse embeddings +- filtering +- case study +--- + +## **Reinventing Restaurant Discovery: How OpenTable built Concierge, an AI Dining Assistant** + +### Recognizing that AI would redefine restaurant discovery + +When generative AI tools entered the mainstream, OpenTable knew diners would change how they find and choose restaurants. People were beginning to expect conversational, intelligent and context-aware assistants, rather than static search boxes. + +Patrick Lombardo, Staff ML Engineer at OpenTable, recalls that the team wanted to move quickly. “We knew early on that generative AI was going to change user expectations. Concierge was an opportunity for us to transform the way that diners discover restaurants while building the tooling and infrastructure that will support future AI-powered experiences.” + +That stepping stone is [Concierge](https://www.opentable.com/blog/concierge-ai-dining-assistant/), an AI-powered assistant designed to answer restaurant-related questions in natural language using OpenTable’s data. + +![Concierge screenshot](/blog/case-study-opentable/opentable-concierge-screenshot.png) + +### Setting clear priorities for accuracy, domain focus, and speed + +For Concierge to succeed, the assistant needed to respond to the vast majority of user questions and every answer had to reflect reality. Incorrect menu items or outdated offerings could erode user and restaurant trust. + +“The primary goal was answerability. We wanted to make sure the model could answer most questions. The second most important was accuracy, so that when the model gave an answer it was correct.” Puyuan Liu, Machine Learning Scientist, OpenTable + +Beyond the application logic, the team needed a vector database that could handle sparse embeddings for keyword expansions and fine-grained filtering. Queries often narrowed results to a single restaurant out of more than 60,000, which placed heavy demands on filtering performance. + +### Qdrant chosen due to sparse embeddings, filtering, and deployment. + +Qdrant emerged as the preferred option for several reasons that aligned directly with OpenTable’s priorities. + +First, its handling of sparse embeddings was a key differentiator. Concierge’s retrieval often required filtering down to a single restaurant, making the collection effectively sparse. Many vector databases see HNSW graph quality degrade under such conditions, but Qdrant’s optimizations avoided that performance drop. + +Second, Qdrant delivered reliable high-precision filtering. In production, each query might target reviews, metadata, and other structured restaurant data all at once. Qdrant handled this with predictable performance, which was essential for hitting their latency budget. + +Third, Qdrant Cloud provided a deployment path that was simpler than self-hosting. + +Patrick Lombardo summed it up: "Creating a Qdrant Cloud cluster was one of the easiest parts of the project. It just worked." + +The production launch was global from the start, allowing Concierge to answer questions about restaurants in many regions without separate deployments. + +### Achieving stability and setting the stage for future innovation + +Concierge met its latency target and maintained high answerability without extensive post-launch tuning. Operationally, Qdrant became one of the most stable components in the stack. Ant White, Principal Software Engineer at OpenTable, explained, “Since running it in production, it is a frictionless part of the stack. ” + +### Key takeaways from the Concierge rollout + +Concierge is continuing to pave the way for OpenTable’s AI transformation. While it is a positive new user experience itself, it also gave OpenTable a safe environment to refine retrieval infrastructure before integrating it into core search. Sparse embeddings proved highly effective when combined with heavy filtering, keeping retrieval precise and efficient. + +Operational stability turned out to be a significant advantage. With Qdrant handling retrieval without incident, the team was free to focus on improving model performance and user experience rather than troubleshooting the database layer. + +With Concierge as a foundation, OpenTable is now positioned to continue to deliver richer, faster, and more intelligent dining experiences, from conversational search to visual dish discovery. \ No newline at end of file diff --git a/qdrant-landing/content/blog/case-study-tavus.md b/qdrant-landing/content/blog/case-study-tavus.md new file mode 100644 index 000000000..0123ce46f --- /dev/null +++ b/qdrant-landing/content/blog/case-study-tavus.md @@ -0,0 +1,60 @@ +--- +draft: false +title: "How Tavus used Qdrant Edge to create conversational AI " +short_description: "Tavus built its Conversational Video Interface with Qdrant edge retrieval to achieve subsecond, human-grade conversations." +description: "Tavus used Qdrant edge retrieval to cut latency and deliver natural, human-grade conversational AI." +preview_image: /blog/case-study-tavus/social_preview_partnership-tavus.jpg +social_preview_image: /blog/case-study-tavus/social_preview_partnership-tavus.jpg +date: 2025-09-12 +author: "Daniel Azoulai" +featured: true + +tags: +- Tavus +- vector search +- conversational AI +- retrieval augmented generation +- latency optimization +- case study +- Edge +--- + +![Tavus Overview](/blog/case-study-tavus/tavus-bento-box-dark.jpg) + +## How Tavus delivered human-grade conversational AI with edge retrieval on Qdrant + +Tavus is a human–computer research lab building CVI, the Conversational Video Interface. CVI presents a face-to-face AI that reads tone, gesture, and on-screen context in real time, allowing for humans to interface with powerful, functional AI like never before. The team’s north star was simple to say and hard to ship: conversations should feel natural. That meant tracking conversational dynamics like utterance-to-utterance timing, back-channeling, and turn-taking while grounding replies in a customer’s private knowledge. + +Early iterations of CVI focused on live conversation quality, but not retrieval. Customers who needed document grounding or recall brought their own RAG layer, which added latency and inconsistency. Tavus wanted to internalize RAG so they could guarantee performance, simplify onboarding, and keep the experience cohesive. + +*“I read your docs, had a clear idea of what to do, implemented it, and it just worked. The simplicity and performance were there from day one.”* + Mert Gerdan, ML Engineer, Tavus + +## Why network hops threatened subsecond conversational flow + +Human conversation tolerates very little lag. Literature on conversational systems shows that in highly engaging exchanges, the optimal time from one speaker finishing to the other starting is about 200ms. For CVI, even 500 to 600ms best case end to end felt tight once you include understanding, planning, text to speech, facial rendering, and streaming. + +Adding network hops for retrieval threatened to push utterance-to-utterance into an unacceptable range. On top of that, customers needed multimodal grounding that spans video, audio, and screen share, as well as per-conversation isolation for security and correctness. The retrieval layer had to be fast, local, and simple. + +## How per-conversation edge vector stores removed latency + +Tavus implemented a self-hosted central Qdrant and spun up per-conversation edge collections that were colocated with the conversational worker. Each conversation generated embeddings on a local GPU and queried its local Qdrant store, which removed the network latency during retrieval. + +### Design choices that balanced speed and quality + +The first choice was to keep both embedding and approximate nearest neighbor lookup local to the node where the conversation runs. Because the data never left the machine, the system avoided serialization and transit delays that would otherwise dominate the latency budget. Most of the usage is in a few collections, and they filter based on conversation id. This made reasoning about context scope easier and created a clean boundary for privacy, auditing, and lifecycle management. + +With the network hop removed, the team chose simplicity over tuning. They did not need quantization or aggressive compression to chase a few extra milliseconds, so they focused on retrieval quality and multimodal accuracy instead. That simplicity enabled a second architectural move: retrieval on every utterance. Each turn could fire an embedding and vector search without imposing a perceptible pause. Finally, Tavus layered speculative execution on top, predicting likely continuations so the agent could prepare responses while the user was still speaking. Together, these choices produced a pipeline that felt natural without sacrificing correctness. + +### Business impact from faster retrieval and smoother launches + +By eliminating the network hop, Tavus reduced retrieval to roughly 20 to 25ms at the edge. End-to-end utterance-to-utterance timing now landed near 500 to 600ms in the best case, leaving enough headroom to add artificial delays for deeper topics where a slower cadence feels more human. Because retrieval was cheap in terms of perceived latency, the team could ground every turn and keep answers accurate even as conversations grew complex. + +The operational picture improved as well. Within the first three weeks, Tavus indexed about 3 to 3.5 million points, with each point representing around 1,500 characters. The launch was uneventful in a good way. Support queues stayed quiet, and customers were able to bring private knowledge into CVI without standing up their own RAG stacks. Developer velocity benefited from a smaller, clearer deployment model that was easier to reason about and extend. + +*“We wanted companies to experience CVI’s quality without building a RAG system themselves. With Qdrant at the edge, retrieval became effectively invisible to the user.”* + Mert Gerdan, ML Engineer, Tavus + +## What the team learned about architecture and speed + +Tavus validated that architecture beats micro-optimizations. Removing the network hop and colocating compute with data produced a larger latency win than squeezing a few milliseconds with quantization. With per-conversation edge stores in place, the team could optimize for quality and safety, such as richer multimodal features and better turn-taking, without sacrificing speed. \ No newline at end of file diff --git a/qdrant-landing/content/blog/decay-functions.md b/qdrant-landing/content/blog/decay-functions.md new file mode 100644 index 000000000..e313c0a2a --- /dev/null +++ b/qdrant-landing/content/blog/decay-functions.md @@ -0,0 +1,258 @@ +--- +title: "Untangling Relevance Score Boosting and Decay Functions" +draft: false +slug: decay-functions # Change this slug to your page slug if needed +short_description: Why's and how's of decay functions in Qdrant's relevance score boosting. # Change this +description: Understanding decay functions for relevance score boosting. # Change this +preview_image: /blog/decay-functions/preview/preview.jpg # Change this +social_preview_image: /blog/decay-functions/preview/social_preview.jpg # Optional image used for link previews +title_preview_image: /blog/decay-functions/preview/title.jpg # Optional image used for blog post title +date: 2025-09-01T14:55:45+02:00 +author: Evgeniya Sukhodolskaya +featured: true +tags: + - features + - tech + - blog + +--- + +A problem we've noticed while monitoring the [Qdrant Discord Community](https://discord.gg/d4MPnX3s) is that due to the extensive list of expressions that the [score boosting](https://qdrant.tech/documentation/concepts/hybrid-queries/#score-boosting) functionality provides, there's room for confusion on how it's supposed to be applied. And that might block you from moving the business logic behind relevance scoring into the Qdrant search engine. We don't want that! + +In this blog, we'd like to de-spooky-fy the **decay functions** part of the score boosting, or, more precisely: `LinDecayExpression`, `ExpDecayExpression`, and `GaussDecayExpression` -- frequent guests on the Discord *#ask-for-help* channel. + +## Purpose of Decay Functions + +Decay functions help turn numeric properties of your dataset items (like sizes or ratings) into values between 1.0 (most relevant) and 0.0 (not relevant). This makes it possible for those properties to meaningfully influence the final relevance score. + +Decay functions are useful when a change in some numeric property of an item should *smoothly* and *proportionally* affect its relevance score. + +Think of it like this: + +- News articles become less relevant over time, so relevance decays with days passed from the publication date. +- A further restaurant is less relevant for food ordering, so relevance decays as the distance to the user increases. +- A better reputation makes a movie more relevant, so relevance decays as the number of positive IMDb reviews decreases. + +### Three Options + +Qdrant has added three decay functions to the score boosting functionality, each one capturing a different way in which relevance can decay. + +{{< figure src="/blog/decay-functions/decay_1.png" alt="Three decay functions used in the score boosting." caption="**Image 1.** Three decay functions used in the score boosting.
An interactive version of this graph is available [here](https://www.desmos.com/calculator/idv5hknwb1)." width="100%" >}} + +**Linear** +Relevance changes at a constant rate with the variable. Each change in the value has the same impact. + +→ For example, the discount percentage: the more, the merrier! + +**Gaussian** +Relevance decays smoothly and gradually. Small deviations from the ideal are forgivable, but the further you go, the less relevant it becomes. + +→ Perfect for things like product price; small differences are usually fine for users, but when the price gap gets big, interest drops off fast. + +**Exponential** +Relevance drops sharply with even small changes. Deviation from the ideal is punished quickly. + +→ Delivery time is a great candidate here. If the item takes too long to arrive, users instantly lose interest. + +**All three decay functions in Qdrant are *symmetrical*. They assign 1.0 relevance to a certain value of a variable and decay toward 0.0 as the variable deviates from this target.** + +This symmetry is useful when there's a clear "ideal" value, and anything more or less than that is equally off. For example: + +- A user has a target price in mind. Anything more expensive is less relevant (obviously), but anything cheaper might also raise quality concerns. (Yes, not always, but many think so.) +- You're searching for a 30-minute exercise video. Both 25 and 35 minutes are okay-ish, but 5 minutes or 1 hour are clearly not what you had in mind. + +## Decay Function Parameters + +To use decay functions for score boosting, you need to figure out what values to provide for their parameters. + +At first glance, it might seem like there are many of them: `x`, `target`, `midpoint`, `scale`... + +Let's demystify these. And let's start with a quick win: two of them, `x` and `target`, we've already used many times in the examples above. + +### `x` parameter + +This is just the variable you want to transform with a decay function: score, time, distance, age, number of reviews, price, etc. + +You can think of it as the *input value*. It may come from a payload field of an item or the embedding similarity score. Basically, it’s the x-axis of the decay function (and y is the output, the relevance score, decaying from 1.0 to 0.0). + +{{< figure src="/blog/decay-functions/decay_2.png" alt="x (x-axis) and target (point on x-axis) of a decay function." caption="**Image 2.** x (x-axis) and target (point on x-axis) of a decay function.
An interactive version of this graph is available [here](https://www.desmos.com/calculator/idv5hknwb1)." width="100%" >}} + +### `target` parameter + +This is the value your `x` variable needs to match for the item to be considered 100% relevant, to get a 1.0 relevance score from the decay function. + +By default, `target` is 0.0, which makes sense in many scenarios. For example: + +- 0.0 meters for delivery distance: the closer, the better. +- 0.0 seconds since publishing: the fresher, the better. + +But of course, `target` can be anything else, depending on your use case: desired and most relevant price, age, size, score, etc. + +*So, in short, `x` is the current value, `target` is the relevance best-case value.* + +### `scale & midpoint` parameters + +Now we're left with `midpoint` and `scale`. What's their purpose? To control how the decay function of your choice will, well... function. Its shape needs to match your definition of relevance and the nature of the variable you're transforming. + +{{< figure src="/blog/decay-functions/decay_3.png" alt="scale (segment on x-axis) and midpoint (point on y-axis), defining the shape of decay functions." caption="**Image 3.** scale (segment on x-axis) and midpoint (point on y-axis), defining the shape of decay functions.
An interactive version of this graph is available [here](https://www.desmos.com/calculator/idv5hknwb1)." width="100%" >}} + +*Together, `scale` and `midpoint` define the slope of the function, how quickly or smoothly relevance decays. It reads: "To what `scale` should `x` change to reach a `midpoint` value of relevance."* + +A decay function's shape is defined by two key points: + +- (`target`, 1.0) --- the ideal use case +- (`target ± scale`, `midpoint`) --- how relevance drops after the `x` variable changes by `scale` from the ideal `target` value. + +The choice of `scale` and `midpoint` defines a certain behavior for each type of decay function. + +- For **Gaussian decay**, relevance drops slowly and smoothly from 1.0 toward `midpoint`, as `x` changes by `scale`. After that, the decay accelerates. +- For **Exponential decay**, it's the opposite: fast decay at first till `midpoint`, then slower. +- For **Linear decay**, `midpoint` and `scale` define the point at which the relevance score hits 0.0, as it's the only decay function that actually reaches zero. + +**Note #1.** `midpoint` defaults to 0.5, but can be anything in the (0.0, 1.0) range. For linear decay, it's also valid to set `midpoint` to 0.0. + +**Note #2.** `scale` defaults to 1.0, but it can be anything that reflects the relationship between your variable `x` and how you define relevance. Only *you* know what makes sense here. + +**Note #3.** We expect `scale` to be a **positive** value; it just makes calculations for us simpler. + +**Note #4.** Exponential and Gaussian decay functions never reach 0.0. The relevance score approaches zero but stays positive. Only Linear decay can reach exactly 0.0, and it's the only one where setting `midpoint` to 0.0 is valid. + +### How to Pick Parameters: Examples + +**Example #1** +**Use case:** A user is searching for educational videos in German about techno club culture to practice language comprehension. They've chosen 5 minutes as the ideal video length. + +**Decay:** Gaussian +**`x`**: Video length in minutes, stored in the video's payload +**`target`**: 5 (*minutes*) +**`scale`**: 4 (*minutes*) +**`midpoint`**: 0.5 + +**Explanation:** +We assume that, out of all videos relevant by content, the user will tolerate deviations in video length by up to ±4 minutes, so 1-minute to 9-minute videos. Relevance should therefore decay *smoothly and slowly* from 1.0 to 0.5 in a Gaussian fashion. +Anything longer than 9 minutes or shorter than 1 minute quickly becomes less relevant, even if the content still matches. + +**Example #2** +**Use case:** A promo code aggregator app boosts freshness to always show the latest promo codes for products and events. + +**Decay:** Exponential +**`x`**: datetime of promo code upload, stored in the payload +**`target`**: Current datetime (moment of search) +**`scale`**: 604800 (1 week in seconds) +**`midpoint`**: 0.1 + +**Explanation:** +Out of all promo codes for different products/events, users will strongly prefer ones uploaded *just now*, as they’re most likely to work. But that relevance drops quickly over time: within a week, it reaches a midpoint of 0.1. After that, if a promo code is still active, it’s a gamble anyway: might work, might not. So old-but-not-expired codes are roughly equally irrelevant. + +**Note #5.** For Qdrant [datetime](https://qdrant.tech/documentation/concepts/payload/#datetime) payloads, `scale` should always be provided in seconds! + +### I Don't Know All the Parameters in Advance + +As you can see, using decay functions in Qdrant's score boosting means you'll have to know the parameters in advance. + +What we've seen in our Discord Community quite a few times is that people try to apply decay functions to normalize similarity scores from [prefetches](https://qdrant.tech/documentation/concepts/hybrid-queries/#multi-stage-queries), usually as a way to fuse results from different types of similarity searches. + +The common question is: + +> How can I use a decay function for score normalization if I don't know the scale in advance? + +The answer is: **you can't dynamically set the `midpoint` and `scale` parameters, hence you can't dynamically normalize scores, and you probably shouldn't even try**: + +Say you're prefetching a subset of items scored by a late interaction model like ColBERT, where a higher score means higher similarity. You get scores like 36, 22, and 1. Separately, you also have cosine similarity scores from some dense vector search, and you'd like to fuse both sets of results. + +If you normalize ColBERT scores dynamically, based on just this subset, 36 will become 1.0, and everything else scales accordingly. + +But here's the problem: That 36 might not be a "high" score at all. Maybe your dataset just didn't contain any good matches. The normalization step will strip away that context, and when you fuse the scores, you'll create a false sense of high relevance. + +*If you're planning to use a decay function for score normalization, you need to know the expected parameter values beforehand. If you don't know the range of your input variable (`x`), you won't be able to use a decay function reliably.* + +## Code Snippets + +Now let's see how using decay functions looks in Qdrant. + +We'll provide HTTP request examples, but you can use decay functions [analogously in the Python, TypeScript, Rust, Java, C#, and Go clients](https://qdrant.tech/documentation/concepts/hybrid-queries/#time-based-score-boosting). + +**Note #6.** +Payload variables used within the formula benefit from having [payload indexes](https://qdrant.tech/documentation/concepts/indexing/#payload-index). So, we require you to set up a payload index for any variable used in a formula. + +Let's take our "educational videos in the German language" example and see how it takes shape in Qdrant: + +```http +POST collections/video/points/query +{ + "prefetch": { + "query": , + "limit": 10 // limit of prefetched results + }, + "query": { + "formula": { + "sum": [ + "$score", // so the final score = score + gauss_decay(duration) + { + "gauss_decay": { + "target": 5, + "scale": 4, + "midpoint": 0.5, + "x": "duration" // payload key + } + } + ] + } + } +} +``` + +And here’s the “fresh promo codes” example, so you’ve got a better grip on how to use Qdrant’s score boosting: + +```http +POST collections/promocodes/points/query +{ + "prefetch": { + "query": , + "limit": 10 // limit of prefetched results + }, + "query": { + "formula": { + "sum": [ + "$score", // so the final score = score + exp_decay(search_time - upload_time) + { + "exp_decay": { + "x": { + "datetime_key": "upload_time" // payload key + }, + "target": { + "datetime": "2025-08-04T00:00:00Z" // time of the search + }, + "scale": 604800, // 1 week in seconds + "midpoint": 0.1 + } + } + ] + } + } +} +``` + +**Note #7.** +`datetime_key` and `datetime` are used to distinguish between payload keys that hold a datetime-type string and directly provided datetime strings. + +## To Sum Up + +So, we've covered quite a bit, including: + +- What decay functions are and why they matter; +- How `target`, `x`, `scale`, and `midpoint` shape decay behavior; +- And how to use decay functions in Qdrant's score boosting. + +We truly hope this write-up helped untangle things a bit. Now the only thing left for you is to get your hands dirty and experiment! + +Use the snippets in the article as a starting point and experiment with the relevance score boosting in [Qdrant Cloud](https://qdrant.tech/). We offer a free-forever 1GB cluster: enough to test, tweak, and see how the decay functions behave on your data. + +And if you feel like diving deeper into decay functions or score boosting in general, check out our [documentation](https://qdrant.tech/documentation/concepts/hybrid-queries/?q=Query+Points+API#score-boosting), which includes a decay-on-distance example and plenty more to learn from. + +### Tell Us What You're Building + +We'd love to hear what you're experimenting with! What are you considering "relevant"? Which features of the last releases are you enjoying? What's missing? + +If you're still unsure about anything, feel free to ask us [on Discord](https://discord.gg/d4MPnX3s) or [connect with me on LinkedIn](https://www.linkedin.com/in/evgeniya-sukhodolskaya/). We're always happy to explain more, and we'd love to know what to write about next! + diff --git a/qdrant-landing/content/blog/mmr-diversity-aware-reranking.md b/qdrant-landing/content/blog/mmr-diversity-aware-reranking.md new file mode 100644 index 000000000..373999d0e --- /dev/null +++ b/qdrant-landing/content/blog/mmr-diversity-aware-reranking.md @@ -0,0 +1,404 @@ +--- +draft: false +title: Balancing Relevance and Diversity with MMR Search +slug: mmr-diversity-aware-reranking +short_description: Discover how Qdrant's Maximum Marginal Relevance (MMR) balances relevance with diversity in fashion search to avoid echo chambers of similar results. +description: Learn how to implement Maximum Marginal Relevance (MMR) with Qdrant to create diverse search results in fashion discovery. This guide shows how to balance relevance and diversity using the DeepFashion dataset and CLIP embeddings. +preview_image: /blog/mmr-diversity-aware-reranking/preview/preview.webp +social_preview_image: /blog/mmr-diversity-aware-reranking/preview/social_preview.jpg +title_preview_image: /blog/mmr-diversity-aware-reranking/preview/title.webp +date: 2025-09-04 +author: Thierry Damiba +featured: true +categories: + - Tutorial + - Vector Search +tags: + - MMR + - Fashion Search + - Diversity + - CLIP + - Vector Search +--- + +Variety is the spice of life! Yet often, with search engines, users find that the results are too similar to get value. You search for a black jacket on your favorite shopping site, and you get 5 black full zip bomber jackets. Search for a black dress and you get 5 strapless dresses. Traditional vector search focuses on returning the most relevant items, which creates an echo chamber of similar results. + +![Similar black dresses](/blog/mmr-diversity-aware-reranking/mmr-food-diversity.webp) + + +*Problem: A search for "black dress" returns only strapless dresses* + +Qdrant's native Maximum Marginal Relevance (MMR) fixes this by balancing similar results with diverse results. Instead of showing variations of the same item, MMR makes sure each result adds something novel to your search. + +While MMR applies to any domain and modality, today we'll explore it through fashion search using the DeepFashion dataset. This visual approach makes the diversity benefits immediately obvious, but the same principles work whether you're searching documents, building recommendation engines, or retrieving context for AI systems. + +For a different perspective on MMR with text-based movie recommendations, check out [Tarun Jain's implementation guide](https://python.plainenglish.io/understanding-maximal-marginal-relevance-mmr-a0a7a8df0a1a). + +## What is Maximum Marginal Relevance? + +![Standard Search returns all "bomber jacket"](/blog/mmr-diversity-aware-reranking/standard-search-results.webp) + + +*Note the diversity of food dishes on the right, with MMR* +MMR solves the redundancy problem by reranking search results based on two criteria: + +1. **Relevance to your query** (how well does this match what you're looking for?) +2. **Diversity from already selected items** (how different is this from what you already have?) + +The algorithm picks the most relevant item first, then for each subsequent item, it balances relevance against similarity to already-selected results. A lambda parameter controls this balance: + +- **λ = 1.0:** Pure relevance (regular vector search) +- **λ = 0.5:** Balanced approach +- **λ = 0.0:** Pure diversity + +*MMR is implemented in Qdrant as a parameter of a nearest neighbor query. Code examples can be found below.* + +## Why Vector Search for Fashion Discovery? + +![Fashion search demonstration](/blog/mmr-diversity-aware-reranking/fashion-search-demo.webp) + +*A search for "black dress" returns only strapless dresses, showing the need for diversity in search results* + +Fashion search is perfect for demonstrating MMR because visual similarity doesn't always match shopping intent. When someone searches for "black jacket," they might want to explore: + +- 🏃 Bomber jacket (sporty) +- 👔 Blazer (professional) +- đŸ§„ Leather jacket (edgy) +- 👖 Denim jacket (casual) + +Your typical search might show four black jackets that look nearly identical. MMR gives you one or two bombers plus diverse alternatives, helping users discover a wider variety of styles. + +Let's take a look at a practical example. + +## Setting Up the Environment +We'll need several libraries for this fashion discovery project: + +```bash +pip install qdrant-client # Vector Search Engine +pip install fastembed # Fast, lightweight embedding generation with CLIP +pip install datasets # For DeepFashion data access +``` + +## Exploring the DeepFashion Dataset +The [DeepFashion dataset](https://huggingface.co/datasets/SaffalPoosh/deepFashion-with-masks) contains over 40,000 clothing images across different categories and styles. It includes rich metadata, such as category, color, and style attributes, that make it perfect for testing diversity algorithms. + +The dataset includes realistic fashion photography: items worn by models, flat lay product shots, and detailed images. The variety of items with similar names makes this dataset perfect for testing whether MMR can distinguish between visually similar items that serve different fashion purposes. + +--- + +## Step 1: Loading and Processing Fashion Data +```python +from datasets import load_dataset +from fastembed import ImageEmbedding, TextEmbedding +from qdrant_client import QdrantClient, models +import uuid + +def load_fashion_data(sample_size=10): + """Load fashion items from DeepFashion-with-masks dataset""" + dataset = load_dataset("SaffalPoosh/deepFashion-with-masks", split="train") + sample_dataset = dataset.shuffle(seed=42).select(range(sample_size)) + + fashion_items = [] + for i, item in enumerate(sample_dataset): + # Extract all available metadata + metadata = {} + for key, value in item.items(): + if key not in ["images", "mask", "mask_overlay"] and value is not None: + metadata[key] = value + + fashion_items.append({ + "image": item["images"], # PIL Image object + **metadata + }) + + return fashion_items + +fashion_items = load_fashion_data(sample_size=100) +``` + +--- + +## Step 2: Creating Fashion Embeddings +We'll use CLIP to create embeddings that understand both visual similarity and semantic meaning in fashion: + +```python +# Create image embeddings for all fashion items +image_model = ImageEmbedding(model_name="Qdrant/clip-ViT-B-32-vision") +embeddings = list(image_model.embed([item["image"] for item in fashion_items])) +``` + +--- + +## Step 3: Setting Up Qdrant for Visual Fashion Search +Create the client and set up your collection + +```python +# Initialize Qdrant client, get your credentials at https://qdrant.tech +client = QdrantClient( + host="your-qdrant url", + api_key="") + +collection_name = "fashion_discovery" + +# Create collection optimized for CLIP embeddings +client.recreate_collection( + collection_name=collection_name, + vectors_config=models.VectorParams( + size=512, # CLIP embedding dimension + distance=models.Distance.COSINE + ) +) + +# Upload fashion items with embeddings +points = [] +for i, (item, embedding) in enumerate(zip(fashion_items, embeddings)): + # Remove PIL image from payload + payload = {k: v for k, v in item.items() if k != "image"} + payload["item_id"] = i + + point = models.PointStruct( + id=str(uuid.uuid4()), + vector=embedding.tolist(), + payload=payload + ) + points.append(point) + +client.upsert(collection_name=collection_name, points=points) +``` + +--- + +## Step 4: Implementing Fashion Search Functions +Now let's create search functions to compare standard similarity versus MMR diversity. We'll use each function with the query: "black jacket" + +### Standard Search +Standard search gives you all similar styles: bomber jacket. + +```python +# Standard fashion search — often returns similar looking items + +def fashion_search_standard(query_text, limit=5): + text_model = TextEmbedding(model_name="Qdrant/clip-ViT-B-32-text") + query_embedding = list(text_model.embed([query_text]))[0] + + results = client.search( + collection_name=collection_name, + query_vector=query_embedding.tolist(), + limit=limit, + with_payload=True + ) + return results + +query_text = "black jacket" + +# Standard search +standard_results = fashion_search_standard(query_text) +print("\nSTANDARD FASHION SEARCH: 'black jacket'") +for i, point in enumerate(standard_results, 1): + payload = point.payload + print(f"{i}ïžâƒŁ Score: {point.score:.4f} | {payload.get('item_description', 'Fashion Item')}") + print(f" Style: {payload.get('style', 'N/A')} | Color: {payload.get('color', 'N/A')}") + print() +``` + +![Standard Search returns all "bomber jacket"](/blog/mmr-diversity-aware-reranking/black-dress-problem.webp) + +*Standard Search returns all "bomber jacket" - notice the lack of diversity in results* + +``` +STANDARD SEARCH RESULTS: "black jacket" + +1ïžâƒŁ Black Bomber Jacket with Orange lining + Style: casual | Color: black | Type: bomber jacket + Score: 0.9234 +` +2ïžâƒŁ Slim-fit Bomber Jacket with Zipper pocket + Style: modern | Color: black | Type: bomber jacket + Score: 0.9156 + +3ïžâƒŁ Lightweight Bomber Jacket with ribbed cuffs + Style: minimalist | Color: black | Type: bomber jacket + Score: 0.9089 + +4ïžâƒŁ Quilted Bomber Jacket with padded lining + Style: classic | Color: black | Type: bomber jacket + Score: 0.9012 + +5ïžâƒŁ Streamlined bomber jacket with sleek zipper + Style: contemporary | Color: black | Type: bomber jacket + Score: 0.8967 +``` + +### MMR Search +MMR gives you diverse styles: a hoodie, a windbreaker, and a blazer. + +```python + # MMR fashion search — balances relevance with style diversity + +def fashion_search_mmr(query_text, limit=5, diversity=0.5): + text_model = TextEmbedding(model_name="Qdrant/clip-ViT-B-32-text") + query_embedding = list(text_model.embed([query_text]))[0] + + results = client.query_points( + collection_name=collection_name, + query=models.NearestQuery( + nearest=query_embedding.tolist(), + mmr=models.Mmr( + diversity=diversity, # 0.0 - relevance; 1.0 - diversity + candidates_limit=100 # num of candidates to preselect + ) + ), + limit=limit, + with_payload=True + ) + return results + +query_text = "black jacket" + +# MMR search with diversity=0.5 +mmr_results = fashion_search_mmr(query_text, diversity=0.5) +print("\nMMR FASHION SEARCH: 'black jacket' (diversity=0.5)") +for i, point in enumerate(mmr_results.points, 1): + payload = point.payload + print(f"{i}ïžâƒŁ Score: {point.score:.4f} | {payload.get('item_description', 'Fashion Item')}") + print(f" Style: {payload.get('style', 'N/A')} | Color: {payload.get('color', 'N/A')}") + print() +``` + +![MMR Search returns different styles](/blog/mmr-diversity-aware-reranking/mmr-search-results.webp) + +*MMR Search returns different styles - bomber jacket, hoodie, windbreaker, and blazer for better diversity* + +``` +MMR SEARCH RESULTS: "black jacket" (diversity=0.5) + +1ïžâƒŁ Black Bomber Jacket with Orange lining + Style: casual | Color: black | Type: bomber jacket + Score: 0.9234 | Selected: Most relevant + +2ïžâƒŁ Black zip-up hoodie with drawstring + Style: relaxed | Color: black | Type: hoodie + Score: 0.8456 | Selected: Different style (hoodie vs bomber) + +3ïžâƒŁ Lightweight Bomber jacket with ribbed cuffs + Style: minimalist | Color: black | Type: bomber jacket + Score: 0.9089 | Selected: Different aesthetic (minimalist) + +4ïžâƒŁ Black Windbreaker with high collar + Style: sporty | Color: black | Type: windbreaker + Score: 0.8234 | Selected: Different function (windbreaker) + +5ïžâƒŁ Black tailored blazer with notch lapel + Style: classic | Color: black | Type: coat + Score: 0.7891 | Selected: Different formality (blazer) +``` + +Keep in mind that MMR selects results one by one. The scores you see in Qdrant are representative of the similarity between each item and the original query. The final ranking won't be sorted by score; it will be sorted by the order in which the MMR algorithm selects each item. + +--- + +## Step 5: Advanced Fashion Filtering with MMR +Combine MMR with Qdrant's metadata-filtering for targeted fashion discovery. In this example, we will filter on two categories. We will search for casual outerwear, but filter on casual and sporty. This allows us to diversify the results while making sure that only outdoor and sporty examples show up. + +```python +def filtered_fashion_search(query_text, metadata_filter=None, limit=5, diversity=0.4): + text_model = TextEmbedding(model_name="Qdrant/clip-ViT-B-32-text") + query_embedding = list(text_model.embed([query_text]))[0] + + # Build filter for metadata fields + filter_conditions = [] + if metadata_filter: + for key, value in metadata_filter.items(): + filter_conditions.append( + models.FieldCondition( + key=key, + match=models.MatchValue(value=value) + ) + ) + + query_filter = models.Filter(must=filter_conditions) if filter_conditions else None + + results = client.query_points( + collection_name=collection_name, + query=models.NearestQuery( + nearest=query_embedding.tolist(), + mmr=models.Mmr( + diversity=diversity, # 0.0 - relevance; 1.0 - diversity + candidates_limit=100 # num of candidates to preselect + ) + ), + query_filter=query_filter, + limit=limit, + with_payload=True + ) + return results +``` + +### Practical Example: Filter for women's blouses + +```python +# Example: Filter for women's blouses +results = filtered_fashion_search( + "professional attire", + metadata_filter={"gender": "WOMEN", "cloth_type": "Blouses_Shirts"}, + diversity=0.5) + +print("FILTERED SEARCH (Women's Professional Attire):") +for point in results.points: + payload = point.payload + print(f"Score: {point.score:.4f}") + print(f"Gender: {payload.get('gender', 'N/A')}") + print(f"Cloth Type: {payload.get('cloth_type', 'N/A')}") + print() +``` + +**Sample Output:** +``` +FILTERED SEARCH (Women's Professional Attire): + +1ïžâƒŁ White Cotton Button-Down Shirt + Score: 0.8934 | Gender: WOMEN | Type: Blouses_Shirts + Style: classic professional + +2ïžâƒŁ Navy Silk Blouse with Bow Tie + Score: 0.8567 | Gender: WOMEN | Type: Blouses_Shirts + Style: elegant business + +3ïžâƒŁ Striped Long-Sleeve Shirt + Score: 0.8234 | Gender: WOMEN | Type: Blouses_Shirts + Style: modern casual-professional +``` + +--- + +## What We Achieved + +1. **Visual embeddings with CLIP** turned fashion images into searchable vectors +2. **MMR reranking** eliminated duplicate-looking recommendations +3. **Diversity control** let us tune exploration vs relevance +4. **Style filtering** combined semantic search with structured metadata for targeted discovery + +The result is a fashion search that actually helps users discover new styles instead of showing variations of the same item. + +--- + +## Where to Go Next + +This pipeline opens up several possibilities: + +- **Visual similarity with style diversity:** Upload a photo and find similar items in different styles +- **Outfit completion:** Given one item, find diverse pieces that create complete outfits +- **Seasonal recommendations:** Balance color preferences with seasonal appropriateness +- **Personal styling AI:** Learn user preferences and recommend diverse items within their taste profile + +--- + +## Try It Yourself + +MMR transforms fashion search from "here are similar items" to "here are diverse options you might love." The native Qdrant implementation handles everything under the hood while giving you fine control over the relevance-diversity balance. + +Start with diversity=0.5 and then adjust based on whether you want more exploration (lower diversity) or precision (higher diversity). + +Try it with your own fashion dataset on [Qdrant Cloud's free tier](https://cloud.qdrant.io/). + +If you enjoyed this article, give me a follow on [LinkedIn](https://www.linkedin.com/in/thierrydamiba/) or [Twitter](https://twitter.com/thierrydamiba) to stay up to date with more guides involving vector search and retrieval optimization. diff --git a/qdrant-landing/content/blog/qdrant-stars-announcement copy.md b/qdrant-landing/content/blog/qdrant-stars-announcement copy.md index ca447c149..6294a2d41 100644 --- a/qdrant-landing/content/blog/qdrant-stars-announcement copy.md +++ b/qdrant-landing/content/blog/qdrant-stars-announcement copy.md @@ -166,13 +166,13 @@ Are you interested in becoming a Qdrant Star? We're on the lookout for individuals who are passionate about vector search technology and looking to make an impact in the AI community. -If you have a strong understanding of vector search technologies, enjoy creating content, speaking at conferences, and actively engage with our community. If this sounds like you, don't hesitate to apply. We look forward to potentially welcoming you as our next Qdrant Star. [Apply here!](https://forms.gle/q4fkwudDsy16xAZk8) +If you have a strong understanding of vector search technologies, enjoy creating content, speaking at conferences, and actively engage with our community. If this sounds like you, don't hesitate to apply. We look forward to potentially welcoming you as our next Qdrant Star. [Apply here!](https://forms.gle/vTuy8Fe9RFdt4SiB9) Share your journey with vector search technologies and how you plan to contribute further. #### Nominate a Qdrant Star -Do you know someone who could be our next Qdrant Star? Please submit your nomination through our [nomination form](https://forms.gle/n4zv7JRkvnp28qv17), explaining why they're a great fit. Your recommendation could help us find the next standout ambassador. +Do you know someone who could be our next Qdrant Star? Please submit your nomination through our [nomination form](hhttps://forms.gle/jsEJ9zjdaxqk7F5b9), explaining why they're a great fit. Your recommendation could help us find the next standout ambassador. #### Learn More diff --git a/qdrant-landing/content/blog/vector-space-day-2025-lineup.md b/qdrant-landing/content/blog/vector-space-day-2025-lineup.md new file mode 100644 index 000000000..5c992bcc8 --- /dev/null +++ b/qdrant-landing/content/blog/vector-space-day-2025-lineup.md @@ -0,0 +1,73 @@ +--- +title: "Announcing the Vector Space Day 2025 Speaker Lineup" +draft: false +slug: vector-space-day-lineup-2025 +short_description: "We are just days away from Vector Space Day in Berlin, and the full speaker lineup is here! " +description: "We are just days away from Vector Space Day in Berlin, and the full speaker lineup is here! This year’s program spans keynotes, deep-dive technical sessions, and lightning talks, covering everything from benchmarking search engines to scalable AI memory and multimodal embeddings." +preview_image: /blog/vector-space-day-2025-lineup/lineup-hero.jpg +social_preview_image: /blog/vector-space-day-2025-lineup/lineup-hero.jpg +date: 2025-09-15 +author: Qdrant +featured: true +tags: + - news + - blog +--- + +# Announcing the Vector Space Day 2025 Speaker Lineup + +We are just days away from [Vector Space Day](https://luma.com/p7w9uqtz) in Berlin, and the full speaker lineup is here\! This year’s program spans keynotes, deep-dive technical sessions, and lightning talks, covering everything from benchmarking search engines to scalable AI memory and multimodal embeddings. Here’s what to expect. + +## Opening Keynotes + +The day begins with perspectives from across the ecosystem: + +* **Andre Zayarni, Andrey Vasnetsov,** and **Neil Kanungo** sharing Qdrant’s vision for the future of vector search and how devs can engage with the Qdrant Community. +* **Robert Eichenseer (Microsoft), Kevin Cochrane (Vultr),** and **Inaam Syed (AWS)** offering insights on how cloud, infrastructure, and developer communities are reshaping AI systems. + +## Breakout Sessions + +#### Track A: Milky Way \- Architectures, Infrastructure and Multimodal Retrieval + +* **AskNews** \- *Building a News Sleuth for the Deep Research Paradigm:* How high-performance hybrid retrieval can support investigative journalism and geopolitical risk monitoring. +* **Delivery Hero** \- *How to Cheat at Benchmarking Search Engines:* Lessons from building reproducible benchmarking harnesses and public leaderboards. +* **Neo4j** \- *Hands-On GraphRAG:* Practical guidance on combining knowledge graphs with RAG for more explainable retrieval. +* **Superlinked** \- *Beyond Text-Only:* How mixture of encoders unlocks advanced retrieval using Google DeepMind’s latest embeddings. +* **Jina AI** \- *Vision-Language Models for Embedding:* Training insights for multimodal embeddings that span text, diagrams, and UI screenshots. +* **TwelveLabs** \- *Practical Multimodal Embeddings:* Real workflows for cross-modal video search and recommendations. +* **Baseten** \- *High Throughput, Low Latency Embedding Pipelines:* Patterns and open-source tools for production-ready embedding inference. +* **Google** **DeepMind** \- *Vector Search with Gemini and EmbeddingGemma:* Deploying cutting-edge embeddings with the right indexing strategies. + +#### Track B: Andromeda \- AI Workflows, Agents and Applications + +* **Linkup** \- *Beyond Web Search:* Infrastructure for AI-native agents that need structured, real-time web intelligence. +* **Cognee** \- *Building Scalable AI Memory:* Abstractions that sync graphs and vectors for durable, multi-backend AI memory. +* **n8n** \- *Evaluate Your Qdrant-RAG Agents:* A live no-code session on agent evaluation using n8n’s native tools. +* **Arize AI** \- *Self-Improving Evaluations:* Feedback loops and tracing for reliable agentic RAG in production. +* **LlamaIndex** \- *Vector Databases for Workflow Engineering:* Using Qdrant to orchestrate context-aware AI pipelines. +* **deepset** \- *Agent-Powered Retrieval with Haystack and Qdrant:* When retrieval agents outperform or overcomplicate pipelines. +* **GoodData** \- *Scaling Real-Time RAG for Analytics:* Lessons from streaming BI artifacts into Qdrant for natural-language analytics. +* **Equal** \- *Redefining Long-Term Memory:* Streaming-driven ingestion architectures that give agents enterprise-grade responsiveness. + +## Lightning Talks + +The afternoon features rapid-fire sessions from innovators including: + +* **bakdata** \- Streaming pipelines with Kafka and Qdrant. +* **KI Reply** \- GDPR-compliant retrieval with graph-augmented RAG. +* **iCompetence** \- Personalized product discovery with multimodal vectors. +* **Raiva** **Technologies** \- Voice-first multimodal search with Qdrant. +* **Superlinked** \- Stories from the AI Search Frontier. + +#### 👉 [**View Full Agenda Details**](https://try.qdrant.tech/hubfs/VSD-2025-program.pdf) + +## Hackathon Awards and After Party + +We will celebrate the winners of the [Think Outside the Bot Hackathon](https://try.qdrant.tech/hackathon-2025), followed by closing remarks and an after party with live DJ and networking. + +## Don’t Miss Out + +Vector Space Day 2025 takes place in Berlin on September 26, 2025\. Space is limited, and registration is filling fast\! + +#### [**Register today**](https://luma.com/p7w9uqtz) + diff --git a/qdrant-landing/content/blog/vector-space-day-2025.md b/qdrant-landing/content/blog/vector-space-day-2025.md index 20e7efd7f..ac970bf83 100644 --- a/qdrant-landing/content/blog/vector-space-day-2025.md +++ b/qdrant-landing/content/blog/vector-space-day-2025.md @@ -5,7 +5,7 @@ slug: vector-space-day-2025 short_description: "We’re hosting our first-ever full-day in-person Vector Space Day this September in Berlin, and you’re invited." description: "From building scalable RAG pipelines to enabling real-time AI memory and next-gen context engineering, we’re covering the full spectrum of modern vector-native search." preview_image: /blog/vector-space-day-2025/Vector-Space-Day-Hero.jpg -social_preview_image: /blog/vector-space-day-2025/partners_6-aug.png.jpg +social_preview_image: /blog/vector-space-day-2025/partners-20.08.png date: 2025-07-14 author: Qdrant featured: false @@ -61,7 +61,7 @@ Missed the deadline? Go ahead and send it in anyway. Proposals submitted after t ### Partners -We’ll be joined by leading organizations including AWS, Microsoft, Vultr, Jina, DeepSet, LlamaIndex, TwelveLabs, n8n, Neo4j, MistralAI, DataTalks.Club, and the MLOps Community, and more. +We’ll be joined by leading organizations including AWS, Microsoft, Vultr, Jina, DeepSet, LlamaIndex, TwelveLabs, n8n, Neo4j, Superlinked, Linkup, DataTalks.Club, and the MLOps Community, and more. These partners represent a cross-section of the most influential players in AI infrastructure and applied research, and we’re proud to collaborate with them to bring this event to life. @@ -69,6 +69,10 @@ Their involvement underscores the growing momentum behind vector search and retr ![Partners](/blog/vector-space-day-2025/Vector-Space-Day-Partners-sept17.jpg) +​Vielen Dank an unseren Medienpartner. Thanks to our media partner. + +![Media](/blog/vector-space-day-2025/media-partner.png) + ### Get Your Ticket General admission: €50 @@ -77,7 +81,9 @@ General admission: €50 Space is limited. -### Global Hackathon - Submissions Closed +### Global Hackathon — Submissions Closed + +![Hackathon](/blog/vector-space-day-2025/hackathon-26aug.png) In the lead-up to Vector Space Day, we're hosting **Think Outside the Bot**, a global, virtual hackathon challenging devs to reimagine what's possible with vector search. Forget the classical RAG chatbot! Explore multi-modal applications, intelligent recommendations, and advanced vector search that go far beyond conversational interfaces. @@ -91,7 +97,6 @@ In the lead-up to Vector Space Day, we're hosting **Think Outside the Bot**, a g [**Submissions are closed but you can learn more.**](https://try.qdrant.tech/hackathon-2025) - ### Need your manager’s approval to attend? We’ve got you covered. Download this ready-to-send request letter to help explain why attending Vector Search Day is a valuable use of your time (and budget). [Download now](https://docs.google.com/document/d/1EivCVK47XEFXAhyoo8QaCBX0Op6uicUODAxTGXhZxrs/edit?usp=sharing). diff --git a/qdrant-landing/content/community/community-features.md b/qdrant-landing/content/community/community-features.md index e99f82293..58600b888 100644 --- a/qdrant-landing/content/community/community-features.md +++ b/qdrant-landing/content/community/community-features.md @@ -1,26 +1,34 @@ --- title: Discover our Programs -resources: +features: - id: 0 + icon: + src: /icons/outline/award-blue.svg + alt: Award title: Qdrant Stars description: Qdrant Stars are our top contributors, organizers, and evangelists. Learn more about how you can become a Star. link: text: Learn More url: /stars/ - image: - src: /img/community-features/qdrant-stars.svg - alt: Avatar - id: 1 + icon: + src: /icons/outline/e-commerce-blue.svg + alt: E-commerce + title: Merch + description: Get your Qdrant t-shirt, hoodie, and more. + link: + text: Shop Now + url: https://www.bystadium.com/us/en/stores/qdrant-swag-for-qdrant-fans-68093/S075175157 +- id: 2 + icon: + src: /icons/outline/world-blue.svg + alt: World title: Vector Space Talks description: Tech talks with Qdrant users and industry experts. link: text: Join Live or Watch On-demand url: https://www.youtube.com/watch?v=4aUq5VnR_VI&list=PL9IXkWSmb36_eANzd_sKeQ3tXbFiEGEWn&pp=iAQB - image: - src: /img/community-features/vector-space-talks.svg - alt: Avatar -features: -- id: 0 +- id: 3 icon: src: /icons/outline/rocket-blue.svg alt: Rocket @@ -29,7 +37,7 @@ features: link: text: Learn More url: https://try.qdrant.tech/events -- id: 1 +- id: 4 icon: src: /icons/outline/guide-blue.svg alt: Guide @@ -38,7 +46,7 @@ features: link: text: Learn More url: https://github.com/qdrant/qdrant/blob/master/docs/CONTRIBUTING.md -- id: 2 +- id: 5 icon: src: /icons/outline/handshake-blue.svg alt: Partners diff --git a/qdrant-landing/content/course/_index.md b/qdrant-landing/content/course/_index.md new file mode 100644 index 000000000..66effde92 --- /dev/null +++ b/qdrant-landing/content/course/_index.md @@ -0,0 +1,4 @@ +--- +--- + +# Welcome to Qdrant Courses \ No newline at end of file diff --git a/qdrant-landing/content/course/essentials/_index.md b/qdrant-landing/content/course/essentials/_index.md new file mode 100644 index 000000000..b9979b666 --- /dev/null +++ b/qdrant-landing/content/course/essentials/_index.md @@ -0,0 +1,105 @@ +--- +title: Qdrant Essentials Course +page_title: Qdrant Essentials Course +description: The ultimate guide to production-grade vector search is here. And it’s free. +content: + sidebarTitle: Qdrant Essentials + menuTitle: + text: Course Overview + url: /course/essentials/ + getStarted: + text: Get Started + url: /course/essentials/day-0/ + nextButton: Continue to Next Video + nextDay: Complete + title: Qdrant Essentials Course + description: The ultimate guide to production-grade vector search is here. And it’s free. +partition: course +--- + +# Qdrant Essentials Course + +The ultimate guide to production-grade vector search is here. And it’s free. + +From your first vector upsert to optimizing high-performance retrieval at scale, this free course takes you from zero to production-ready. Learn how to build efficient vector search, fine-tune Qdrant for maximum performance, and keep your system lightweight, even when working with billions of vectors. + +{{< course-card +title="Skills you’ll gain:" +image="/icons/outline/training-white.svg" +isWideList="true">}} +- Vector search fundamentals +- Performance optimization +- Hybrid and similarity search +- Portfolio project development +{{< /course-card >}} + +## What Is the Course? + +No matter if you're exploring vector search for the first time or fine-tuning a large-scale RAG system, this free course gives you the practical foundation and advanced skills you need. + +Over 9 days (plus bonus content), you’ll build up from the fundamentals to advanced deployment strategies with Qdrant. Each module focuses on a single concept or capability, paired with a hands-on exercise to apply what you’ve learned. You will start with basics, build confidence, and gradually progress to complex topics.  + +Every day includes a hands-on exercise or mini-project, like creating a collection, uploading points, building a hybrid search pipeline, or tuning the HNSW index. + +At the end, you’ll bring everything together by building a full production-grade vector search application. You’ll graduate with a portfolio-worthy project, plus a deep understanding of how to apply Qdrant in production scenarios. + +## Course Overview + +{{< accordion >}} +- title: "Days 0: Setup, Orientation & “Hello Qdrant!”" + content: | + - Welcome & Course Orientation + - Environment Setup + - Mini “Hello Qdrant!” Demo + +- title: "Day 1: Core Qdrant Data Model & Vector Search 101" + content: Content + +- title: "Days 2: Indexing & Vector Storage Architecture" + content: Content + +- title: "Day 3: Hybrid Search" + content: Content + +- title: "Day 4: Optimizations & Query APIs" + content: Content +{{< /accordion >}} + +## Certificate of Completion + +image + +## Who Is the Course For? + +You! But really, this course is great for hands-on professionals who need to build or improve applications with semantic or hybrid search capabilities, or developers exploring vector databases for the first time. + +If your job title includes: + +- Machine Learning Engineer +- Backend Developer +- Data Engineer +- Search Engineer +- MLOps Engineer + +you’re in the right spot. + +## Pre-Reqs + +You don’t need prior Qdrant or vector database experience, but you should be comfortable with: +- Basic Python programming +- Running commands in your terminal +- Working with APIs or Python SDKs +- Some ML background (e.g., embeddings) + +Optional but helpful: +- Docker basics +- Experience with search systems or deploying applications + +{{< course-card +title="Why Start Today" +image="/icons/outline/rocket-white-light.svg" +link="/course/day-0/">}} +- Seeing practical examples (e.g., hybrid search, sparse+dense vectors) +- Learning key deployment tactics (multi-node clusters, on-disk indexing, RBAC) +- Building a final portfolio-grade project to showcase +{{< /course-card >}} diff --git a/qdrant-landing/content/course/essentials/certification.md b/qdrant-landing/content/course/essentials/certification.md new file mode 100644 index 000000000..95de4d9de --- /dev/null +++ b/qdrant-landing/content/course/essentials/certification.md @@ -0,0 +1,6 @@ +--- +title: Certification +url: /course/certification/ +--- + +# Certification diff --git a/qdrant-landing/content/course/essentials/day-0/_index.md b/qdrant-landing/content/course/essentials/day-0/_index.md new file mode 100644 index 000000000..d8af202fd --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-0/_index.md @@ -0,0 +1,13 @@ +--- +title: Day 0 +isLesson: true +weight: 1 +--- + +{{< date >}} Day 0 {{< /date >}} + +# Setup, Orientation & “Hello Qdrant!” + +Welcome to Day 0! Today we’ll lay the foundation for everything in the course. Let’s get started with setting up your environment, and creating your first collection. By the end of this, you’ll have your first similarity search running! In total, the three videos are about 20 minutes total. + +## Video 01: Course Overview diff --git a/qdrant-landing/content/course/essentials/day-0/video-02.md b/qdrant-landing/content/course/essentials/day-0/video-02.md new file mode 100644 index 000000000..d3a511f4c --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-0/video-02.md @@ -0,0 +1,9 @@ +--- +title: Video 02 +weight: 2 +--- + +{{< date >}} Day 0 {{< /date >}} + +# Video 02: Set Up Your Environment + diff --git a/qdrant-landing/content/course/essentials/day-0/video-03.md b/qdrant-landing/content/course/essentials/day-0/video-03.md new file mode 100644 index 000000000..feb96d353 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-0/video-03.md @@ -0,0 +1,9 @@ +--- +title: Video 03 +weight: 3 +--- + +{{< date >}} Day 0 {{< /date >}} + +# Video 03: Create a Collection + diff --git a/qdrant-landing/content/course/essentials/day-1/_index.md b/qdrant-landing/content/course/essentials/day-1/_index.md new file mode 100644 index 000000000..e37852126 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-1/_index.md @@ -0,0 +1,9 @@ +--- +title: Day 1 +isLesson: true +weight: 4 +--- + +{{< date >}} Day 1 {{< /date >}} + +# Day 1 diff --git a/qdrant-landing/content/course/essentials/day-1/video-02.md b/qdrant-landing/content/course/essentials/day-1/video-02.md new file mode 100644 index 000000000..4ca068e89 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-1/video-02.md @@ -0,0 +1,9 @@ +--- +title: Video 02 +weight: 5 +--- + +{{< date >}} Day 1 {{< /date >}} + +# Video 02: Set Up Your Environment + diff --git a/qdrant-landing/content/course/essentials/day-2/_index.md b/qdrant-landing/content/course/essentials/day-2/_index.md new file mode 100644 index 000000000..078a6af7d --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-2/_index.md @@ -0,0 +1,9 @@ +--- +title: Day 2 +isLesson: true +weight: 6 +--- + +{{< date >}} Day 2 {{< /date >}} + +# Day 2 diff --git a/qdrant-landing/content/course/essentials/day-3/_index.md b/qdrant-landing/content/course/essentials/day-3/_index.md new file mode 100644 index 000000000..7709f3a5c --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/_index.md @@ -0,0 +1,9 @@ +--- +title: Day 3 +isLesson: true +weight: 7 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Day 3 diff --git a/qdrant-landing/content/course/essentials/day-4/_index.md b/qdrant-landing/content/course/essentials/day-4/_index.md new file mode 100644 index 000000000..31d948cb4 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-4/_index.md @@ -0,0 +1,9 @@ +--- +title: Day 4 +isLesson: true +weight: 8 +--- + +{{< date >}} Day 4 {{< /date >}} + +# Day 4 diff --git a/qdrant-landing/content/course/essentials/day-9/_index.md b/qdrant-landing/content/course/essentials/day-9/_index.md new file mode 100644 index 000000000..e08cb2a85 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-9/_index.md @@ -0,0 +1,59 @@ +--- +title: Day 9 (Bonus) +isLesson: true +weight: 9 +--- + +{{< date >}} Day 9 {{< /date >}} + +# Bonus: Advanced Configurations + +{{< cards-list >}} +- icon: /courses/course-integrations/crew-ai.svg + title: Building Agents with CrewAI and Qdrant + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/haystack.svg + title: Haystack + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/jina.svg + title: Jina + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/n8n.svg + title: n8n + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/camel-ai.svg + title: Camel-AI + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/tensorlake.svg + title: Tensorlake + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/vectorize.svg + title: Vectorize.io + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/unstructured.svg + title: Unstructured.io + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/quotient.svg + title: Quotient + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/superlinked.svg + title: Superlinked + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/twelveLabs.svg + title: TwelveLabs + content: Qdrant is compatible with Cohere co.embed API. + +- icon: /courses/course-integrations/aparavi.svg + title: APARAVI + content: Qdrant is compatible with Cohere co.embed API. +{{< /cards-list >}} diff --git a/qdrant-landing/content/course/essentials/faq.md b/qdrant-landing/content/course/essentials/faq.md new file mode 100644 index 000000000..c7a24f8df --- /dev/null +++ b/qdrant-landing/content/course/essentials/faq.md @@ -0,0 +1,51 @@ +--- +title: FAQ +url: /course/faq/ +--- + +# Qdrant Essentials Course FAQs + +## Who is this course for? + +Engineers and developers working with semantic or hybrid search, building LLM pipelines, or exploring vector databases for the first time. No prior experience with Qdrant or vector search is needed. + +## Is the course free? + +Yes, completely free. No credit card required. + +## Do I get a certificate of completion? + +Yes! But just watching the videos doesn’t quite get you there. You need to [[insert instructions]](/#) to obtain the certification. And, you’ll also have your project to add to your portfolio! + +## How much time should I spend on this course? + +Each “Day” of this course is designed to be completed in 1 day. Day 0 is the quickest, and then there are 8 days of content. So you are looking at a minimum of 9 days, but don’t forget about the bonus content. + +The good news is that you can take as much time as you need to complete the course since it is on-demand and at your own pace. + +## What tools do I need? + +Python, Docker, and optionally Qdrant Cloud. We walk you through setup on Day 0. + +## Can I use Qdrant locally or do I need a cloud account? + +You can do either. We support both local (via Docker) and Qdrant Cloud setups. + +## What if I get stuck or have a question? + +Join the Qdrant Discord. Our team and community are ready to help. + +## Where can I get the code for the course? + +Look at each Day’s page of content. You can also find them in this repo. + +{{< course-card +title="Why Start Today" +image="/icons/outline/rocket-white-light.svg" +link="/course/day-0/">}} + +- Seeing practical examples (e.g., hybrid search, sparse+dense vectors) +- Learning key deployment tactics (multi-node clusters, on-disk indexing, RBAC) +- Building a final portfolio-grade project to showcase + +{{< /course-card >}} diff --git a/qdrant-landing/content/documentation/cloud-pricing-payments.md b/qdrant-landing/content/documentation/cloud-pricing-payments.md index bf4f57ded..639e34842 100644 --- a/qdrant-landing/content/documentation/cloud-pricing-payments.md +++ b/qdrant-landing/content/documentation/cloud-pricing-payments.md @@ -19,7 +19,7 @@ You can pay for your Qdrant Cloud database clusters either with a credit card or Your payment method is charged at the beginning of each month for the previous month's usage. There is no difference in pricing between the different payment methods. -If you choose to pay through a marketplace, the Qdrant Cloud usage costs are added as usage units to your existing billing for your cloud provider services. A detailed breakdown of your usage is available in the Qdrant Cloud Console. +If you choose to pay through a marketplace, the Qdrant Cloud usage costs are added as $0.01 Resource Usage Units to your existing billing for your cloud provider services. E.g. if you create a Qdrant Cluster that costs $85 in a month, 8,500 Resource Usage Units for Qdrant Cloud will be added to your cloud provider bill. A detailed breakdown of your usage is available in the Qdrant Cloud Console. Note: Even if you pay using a marketplace subscription, your database clusters will still be deployed into Qdrant-owned infrastructure. The setup and management of Qdrant database clusters will also still be done via the Qdrant Cloud Console UI. @@ -29,7 +29,7 @@ If you wish to deploy Qdrant database clusters into your own environment from Qd ### Credit Card -Credit card payments are processed through Stripe. To set up a credit card, go to the Billing Details screen in the [Qdrant Cloud Console](https://cloud.qdrant.io/), select **Stripe** as the payment method, and enter your credit card details. +Credit card payments are processed through Stripe. To set up a credit card, go to the Billing Details screen in the [Qdrant Cloud Console](https://cloud.qdrant.io/), select **Credit Card** as the payment method, and enter your credit card details. ### AWS Marketplace diff --git a/qdrant-landing/content/documentation/cloud-rbac/permission-reference.md b/qdrant-landing/content/documentation/cloud-rbac/permission-reference.md index 7c0de50c2..48001d4eb 100644 --- a/qdrant-landing/content/documentation/cloud-rbac/permission-reference.md +++ b/qdrant-landing/content/documentation/cloud-rbac/permission-reference.md @@ -54,6 +54,12 @@ Permissions for API Keys, backups, clusters, and backup schedules. | `write:clusters` | Modify cluster settings. | | `delete:clusters` | Delete clusters. | +### **Cluster Data** +| Permission | Description | +|------------|------------| +| `read:cluster_data` | View cluster data, used for the Cluster UI button on Cluster Details. [Maps to global `read-only` JWT access for the cluster.](/documentation/guides/security/) | +| `write:cluster_data` | View and modify cluster data, used for the Cluster UI button on Cluster Details. [Maps to global `read-write` JWT access for the cluster.](/documentation/guides/security/) | + ### **Backup Schedules** | Permission | Description | |------------|------------| diff --git a/qdrant-landing/content/documentation/cloud-security.md b/qdrant-landing/content/documentation/cloud-security.md new file mode 100644 index 000000000..ed16268ab --- /dev/null +++ b/qdrant-landing/content/documentation/cloud-security.md @@ -0,0 +1,71 @@ +--- +title: Security +weight: 36 +partition: cloud +aliases: + - /documentation/cloud/security/ +--- + +# Qdrant Cloud Security + +## Compliance and Certifications + +Qdrant is committed to maintaining high standards of security and compliance. We are both SOC2 Type 2 and HIPAA certified, ensuring that our systems and processes meet rigorous security criteria. You can find our compliance reports in our [Trust Center](https://qdrant.to/trust-center). The trust center also contains our internal security policies and procedures, so you can learn how we manage data protection, vulnerabilities, disaster recovery, incident responses, and more. + +## Security Considerations + +### Managed Cloud + +All Qdrant clusters running in Qdrant Managed Cloud are isolated from each other in hardened, unprivileged containers. Each cluster is sealed off with strict network policies, ensuring that no other customer can access your data, and outbound network access is restricted to prevent data exfiltration. Paid clusters are running on their own dedicated resources to ensure stable performance and further security. + +All storage volumes are encrypted at rest. [Premium customers](/documentation/cloud-premium/) can also bring their own encryption keys for storage volumes. + +Data in transit is protected with Transport-Layer-Security (TLS). It is possible to restrict the [IP ranges](/cloud/configure-cluster/#client-ip-restrictions) that are allowed to access a cluster. + +Infrastructure access is restricted and audited according to the policies described in our [Trust Center](https://qdrant.to/trust-center). All infrastructure components are regularly patched and updated to ensure up to date security. + +Access to Qdrant Cloud accounts can be configured with granular [Role-Based Access Control](/documentation/cloud-rbac/). [Premium customers](/documentation/cloud-premium/) can also enable [Single Sign-On (SSO)](/documentation/cloud-account-setup/#enterprise-single-sign-on-sso) with their identity provider. + +[API keys](/cloud/authentication/) can be configured with granular access controls and rotated at any time. API Keys are never stored in plaintext. + +We recommend configuring an expiration date and rotating your API keys regularly as a security best practice. + +### Hybrid Cloud + +For Qdrant Hybrid Cloud, the same security considerations as Managed Cloud apply. The key difference is that since the data plane is running in your own infrastructure, you as the customer are responsible for the security of the underlying infrastructure. + +Qdrant Hybrid Cloud was built for security minded organizations where being "air gapped" is not a hard requirement, but there may be complex compliance and security controls that require you to operate your own infrastructure. It provides a similar developer and ops experience in the Qdrant Cloud Console to Managed Cloud, while allowing organizations to adhere to overarching security requirements with the data plane. + +Please refer to our [Hybrid Cloud documentation](/documentation/hybrid-cloud/) for more details. + +Qdrant clusters in Hybrid Cloud also run in hardened, unprivileged containers with strict network policies. Clusters run entirely on your own infrastructure, within your network, with your own controlled storage. Qdrant does not have any access to the database, any stored data, API keys, backups or database logs. Telemetry data and details such as the cluster configuration are shared with Qdrant to allow the Qdrant Cloud Console to be used for administration. + +### Private Cloud + +In Qdrant Private Cloud, Qdrant clusters run completely isolated and air-gapped within your infrastucture without any connection to the Qdrant Cloud Console. + +Since there is no connection or communication with Qdrant, you are fully responsible for the security of the entire Qdrant Private Cloud installation. This also means that you do not benefit from the integrated management and observability features of Qdrant Managed Cloud and Hybrid Cloud. + +Please refer to our [Private Cloud documentation](/documentation/private-cloud/) for more details. As Private Cloud is completely isolated from the Qdrant Cloud Console, we encourage you to assess your security requirements against Hybrid Cloud before considering Private Cloud for the best experience. + +With Private Cloud, Qdrant has no access to the database, any stored data, API keys, backups or the cluster logs. Compared to Hybrid Cloud, telemetry data is not shared with Qdrant and it is not possible to use the Qdrant Cloud Console to manage your clusters. + +## Software Bill of Materials (SBOM) and Container Image Security + +We provide a Software Bill of Materials (SBOM) for all Qdrant clusters and Qdrant Cloud components running on your infrastructure in Qdrant Hybrid Cloud and Qdrant Private Cloud. An SBOM is attached to every released container image. You can inspect it with the tool of your choice, e.g.: + +```bash +docker buildx imagetools inspect registry.cloud.qdrant.io/qdrant/operator:latest --format "{{ json .SBOM }}" +``` + +All of our container images are scanned for vulnerabilities using [Trivy](https://trivy.dev/). + +All of our container images and helm charts are signed using [Cosign](https://docs.sigstore.dev/). You can verify the signature of an image with `cosign`: + +```bash +cosign verify registry.cloud.qdrant.io/qdrant/operator:latest --certificate-oidc-issuer=https://token.actions.githubusercontent.com --certificate-identity-regexp='https://github.com/qdrant/.*' +``` + +## Terms of Service and Data Processing Agreement + +By using Qdrant Cloud, you agree to our [Terms of Service](https://cloud.qdrant.io/service-agreement) and [Privacy Policy](https://cloud.qdrant.io/privacy-policy). For customers subject to GDPR, we also offer a [Data Processing Agreement (DPA)](https://cloud.qdrant.io/dpa) that outlines our commitments regarding data protection and privacy. diff --git a/qdrant-landing/content/documentation/cloud-tools/pulumi.md b/qdrant-landing/content/documentation/cloud-tools/pulumi.md index c5af3ae89..78c6c7917 100644 --- a/qdrant-landing/content/documentation/cloud-tools/pulumi.md +++ b/qdrant-landing/content/documentation/cloud-tools/pulumi.md @@ -36,191 +36,76 @@ pulumi package add terraform-provider registry.terraform.io/qdrant/qdrant-cloud pulumi config set qdrant-cloud:apiKey "" --secret ``` -- You can now import the SDK as: +## Example Usage + +The following example creates a new Qdrant cluster in Google Cloud Platform (GCP) and returns the URL of the cluster. ```python +import pulumi import pulumi_qdrant_cloud as qdrant_cloud + +all_packages = qdrant_cloud.index.getBookingPackages.get_booking_packages(cloud_provider="gcp", + cloud_region="us-east4") +desired_package = [pkg for pkg in all_packages["packages"] if pkg["resourceConfiguration"][0]["cpu"] == "16000m" and pkg["resourceConfiguration"][0]["ram"] == "64Gi"] +example = qdrant_cloud.AccountsCluster("example", + name="tf-example-cluster", + cloud_provider=all_packages["cloudProvider"], + cloud_region=all_packages["cloudRegion"], + configuration={ + "number_of_nodes": 1, + "database_configuration": { + "service": { + "jwt_rbac": True, + }, + }, + "node_configuration": { + "package_id": desired_package[0]["id"], + }, + }) +example_accounts_database_api_key_v2 = qdrant_cloud.AccountsDatabaseApiKeyV2("example", + cluster_id=example.id, + name="example-key") +pulumi.export("url", example.url) +pulumi.export("key", example_accounts_database_api_key_v2.key) + ``` ```typescript -import * as qdrantCloud from "qdrant-cloud"; -``` +import * as pulumi from "@pulumi/pulumi"; +import * as qdrant_cloud from "@pulumi/qdrant-cloud"; -```java -import com.pulumi.qdrantcloud.*; -``` - -## Usage - -The provider includes the following data-sources and resources to work with: - -### Data Sources - -- `qdrant-cloud_booking_packages` - Get IDs and detailed information about the packages/subscriptions available. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/booking_packages.md) - -```python -qdrant_cloud.get_booking_packages(cloud_provider="aws", cloud_region="us-west-2") -``` - -```typescript -qdrantCloud.getBookingPackages({ - cloudProvider: "aws", - cloudRegion: "us-west-2" -}) -``` - -```java -import com.pulumi.qdrantcloud.inputs.GetBookingPackagesArgs; - -QdrantcloudFunctions.getBookingPackages(GetBookingPackagesArgs.builder() - .cloudProvider("aws") - .cloudRegion("us-west-2") - .build()); -``` - -- `qdrant-cloud_accounts_auth_keys` - List API keys for Qdrant clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_auth_keys.md) - -```python -qdrant_cloud.get_accounts_auth_keys(account_id="") -``` - -```typescript -qdrantCloud.getAccountsAuthKeys({ - accountId: "" -}) -``` - -```java -import com.pulumi.qdrantcloud.inputs.GetAccountsAuthKeysArgs; - -QdrantcloudFunctions.getAccountsAuthKeys(GetAccountsAuthKeysArgs.builder() - .accountId("") - .build()); -``` - -- `qdrant-cloud_accounts_cluster` - Get Cluster Information. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_cluster.md) - -```python -qdrant_cloud.get_accounts_cluster( - account_id="", - id="", -) -``` - -```typescript -qdrantCloud.getAccountsCluster({ - accountId: "", - id: "" -}) -``` - -```java -import com.pulumi.qdrantcloud.inputs.GetAccountsClusterArgs; - -QdrantcloudFunctions.getAccountsCluster(GetAccountsClusterArgs - .builder() - .accountId("") - .id("") - .build()); -``` - -- `qdrant-cloud_accounts_clusters` - List Qdrant clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_clusters.md) - -```python -qdrant_cloud.get_accounts_clusters(account_id="") -``` - -```typescript -qdrantCloud.getAccountsClusters({ - accountId: "" -}) -``` - -```java -import com.pulumi.qdrantcloud.inputs.GetAccountsClustersArgs; - -QdrantcloudFunctions.getAccountsClusters( - GetAccountsClustersArgs.builder().accountId("").build()); -``` - -### Resources - -- `qdrant-cloud_accounts_cluster` - Create clusters on Qdrant cloud - [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/resources/accounts_cluster.md) - -```python -qdrant_cloud.AccountsCluster( - resource_name="pl-example-cluster-resource", - name="pl-example-cluster", - cloud_provider="gcp", - cloud_region="us-east4", - configuration=qdrant_cloud.AccountsClusterConfigurationArgs( - number_of_nodes=1, - node_configuration=qdrant_cloud.AccountsClusterConfigurationNodeConfigurationArgs( - package_id="3920d1eb-d3eb-4117-9578-b12d89bb1c5d" - ), - ), - account_id="", -) -``` - -```typescript -new qdrantCloud.AccountsCluster("pl-example-cluster-resource", { +const allPackages = qdrant_cloud.index.getBookingPackages.getBookingPackages({ cloudProvider: "gcp", cloudRegion: "us-east4", +}); +const desiredPackage = allPackages.packages.filter(pkg => pkg.resourceConfiguration[0].cpu == "16000m" && pkg.resourceConfiguration[0].ram == "64Gi"); +const example = new qdrant_cloud.AccountsCluster("example", { + name: "tf-example-cluster", + cloudProvider: allPackages.cloudProvider, + cloudRegion: allPackages.cloudRegion, configuration: { numberOfNodes: 1, + databaseConfiguration: { + service: { + jwtRbac: true, + }, + }, nodeConfiguration: { - packageId: "3920d1eb-d3eb-4117-9578-b12d89bb1c5d" - } + packageId: desiredPackage[0].id, + }, }, - accountId: "" -}) -``` - -```java -import com.pulumi.qdrantcloud.AccountsClusterArgs; -import com.pulumi.qdrantcloud.inputs.AccountsClusterConfigurationArgs; -import com.pulumi.qdrantcloud.inputs.AccountsClusterConfigurationNodeConfigurationArgs; - -new AccountsCluster("pl-example-cluster-resource", AccountsClusterArgs.builder() - .name("pl-example-cluster") - .cloudProvider("gcp") - .cloudRegion("us-east4") - .configuration(AccountsClusterConfigurationArgs.builder() - .numberOfNodes(1.0) - .nodeConfiguration(AccountsClusterConfigurationNodeConfigurationArgs.builder() - .packageId("3920d1eb-d3eb-4117-9578-b12d89bb1c5d") - .build()) - .build()) - .accountId("") - .build()); -``` - -- `qdrant-cloud_accounts_auth_key` - Create API keys for Qdrant cloud clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/resources/accounts_auth_key.md) - -```python -qdrant_cloud.AccountsAuthKey( - resource_name="pl-example-key-resource", - cluster_ids=[""], -) -``` - -```typescript -new qdrantCloud.AccountsAuthKey("pl-example-cluster-resource", { - clusterIds: ["", ""] -}) -``` - -```java -import com.pulumi.qdrantcloud.AccountsAuthKey; -import com.pulumi.qdrantcloud.AccountsAuthKeyArgs; - -new AccountsAuthKey("pl-example-key-resource", AccountsAuthKeyArgs.builder() - .clusterIds("", "") - .build()); +}); +const exampleAccountsDatabaseApiKeyV2 = new qdrant_cloud.AccountsDatabaseApiKeyV2("example", { + clusterId: example.id, + name: "example-key", +}); +export const url = example.url; +export const key = exampleAccountsDatabaseApiKeyV2.key; ``` ## Further Reading +The provider documentation contains more details on the available resources and data sources, including additional examples: + - [Provider Documentation](https://registry.terraform.io/providers/qdrant/qdrant-cloud/latest/docs) - [Pulumi Quickstart](https://www.pulumi.com/docs/get-started/) diff --git a/qdrant-landing/content/documentation/cloud-tools/terraform.md b/qdrant-landing/content/documentation/cloud-tools/terraform.md index d20a66fe2..f8e69d923 100644 --- a/qdrant-landing/content/documentation/cloud-tools/terraform.md +++ b/qdrant-landing/content/documentation/cloud-tools/terraform.md @@ -37,42 +37,56 @@ provider "qdrant-cloud" { account_id = "QDRANT_ACCOUNT_ID>" // Account ID from cloud.qdrant.io/accounts// (can be overriden on resource level) } +data "qdrant-cloud_booking_packages" "all_packages" { + cloud_provider = "gcp" // Required. Please refer to the documentation (https://registry.terraformio/providers/qdrant/qdrant-cloud/latest/docs/guides/getting-started) for the available options. + cloud_region = "us-east4" // Required. Please refer to the documentation (https://registry.terraformio/providers/qdrant/qdrant-cloud/latest/docs/guides/getting-started) for the available options. +} + +locals { + desired_package = [ + for pkg in data.qdrant-cloud_booking_packages.all_packages.packages : pkg + if pkg.resource_configuration[0].cpu == "16000m" && pkg.resource_configuration[0].ram == "64Gi" + ] +} + + resource "qdrant-cloud_accounts_cluster" "example" { name = "tf-example-cluster" - cloud_provider = "gcp" - cloud_region = "us-east4" + cloud_provider = data.qdrant-cloud_booking_packages.all_aws_eu_west_1_packages.cloud_provider + cloud_region = data.qdrant-cloud_booking_packages.all_aws_eu_west_1_packages.cloud_region configuration { number_of_nodes = 1 + database_configuration { + service { + jwt_rbac = true + } + } node_configuration { - package_id = "7c939d96-d671-4051-aa16-3b8b7130fa42" + package_id = local.desired_package[0].id } } } +resource "qdrant-cloud_accounts_database_api_key_v2" "example" { + cluster_id = qdrant-cloud_accounts_cluster.example.id + name = "example-key" +} + + output "url" { value = qdrant-cloud_accounts_cluster.example.url } + +output "key" { + value = qdrant-cloud_accounts_database_api_key_v2.example.key + description = "Key is available only once, after creation." +} + ``` -The provider includes the following resources and data-sources to work with: - -## Resources - -- `qdrant-cloud_accounts_cluster` - Create clusters on Qdrant cloud - [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/resources/accounts_cluster.md) - -- `qdrant-cloud_accounts_auth_key` - Create API keys for Qdrant cloud clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/resources/accounts_auth_key.md) - -## Data Sources - -- `qdrant-cloud_accounts_auth_keys` - List API keys for Qdrant clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_auth_keys.md) - -- `qdrant-cloud_accounts_cluster` - Get Cluster Information. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_cluster.md) - -- `qdrant-cloud_accounts_clusters` - List Qdrant clusters. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/accounts_clusters.md) - -- `qdrant-cloud_booking_packages` - Get detailed information about the packages/subscriptions available. [Reference](https://github.com/qdrant/terraform-provider-qdrant-cloud/blob/main/docs/data-sources/booking_packages.md) - ## Further Reading +The provider documentation contains more details on the available resources and data sources, including additional examples: + - [Provider Documentation](https://registry.terraform.io/providers/qdrant/qdrant-cloud/latest/docs) - [Terraform Quickstart](https://developer.hashicorp.com/terraform/tutorials) diff --git a/qdrant-landing/content/documentation/cloud/cluster-access.md b/qdrant-landing/content/documentation/cloud/cluster-access.md index 42249c629..03280882b 100644 --- a/qdrant-landing/content/documentation/cloud/cluster-access.md +++ b/qdrant-landing/content/documentation/cloud/cluster-access.md @@ -9,7 +9,7 @@ Once you [created](/documentation/cloud/create-cluster/) a cluster, and set up a ## Cluster UI -There is the convenient link on the cluster detail page in the Qdrant Cloud Console to access the [Cluster UI](/documentation/web-ui/). +You can access your [Cluster UI](/documentation/web-ui/) via the Cluster Details page in the Qdrant Cloud Console. Authentication to a cluster is automatic if your cloud user has the [`read:cluster_data` or `write:cluster_data` permission](/documentation/cloud-rbac/permission-reference/). Without the correct permissions you will be prompted to enter an [API Key](/documentation/cloud/authentication/) to access the cluster. ![Cluster Cluster UI](/documentation/cloud/cloud-db-dashboard.png) diff --git a/qdrant-landing/content/documentation/cloud/configure-cluster.md b/qdrant-landing/content/documentation/cloud/configure-cluster.md index a0d245b33..59c233301 100644 --- a/qdrant-landing/content/documentation/cloud/configure-cluster.md +++ b/qdrant-landing/content/documentation/cloud/configure-cluster.md @@ -7,6 +7,14 @@ weight: 55 Qdrant Cloud offers several advanced configuration options to optimize clusters for your specific needs. You can access these options from the Cluster Details page in the Qdrant Cloud console. +The cloud platform does not expose all [configuration options](/documentation/guides/configuration/) available in Qdrant. We have selected the relevant options that are explained in detail below. + +In adition the cloud platform automatically configures the following settings for your cluster to ensure optimal performance and reliability: + +* The maximum number of collections in a cluster is set to 1000. Larger numbers of collections lead to performance degradation. For more information see [Multitenancy](/documentation/guides/multiple-partitions/). +* Strict mode is activated by default for new collections enforcing that all filters being used in retrieve and udpate queries are indexed. This improves performance and reliability. You can disable this individually for each collection. For more information see [Strict Mode](/documentation/guides/administration/#strict-mode). +* The cluster mode is automatically enabled to allow distributed deployments and horizontal scaling. + ## Collection Defaults You can set default values for the configuration of new collections in your cluster. These defaults will be used when creating a new collection, unless you override them in the collection creation request. @@ -17,9 +25,19 @@ Refer to [Qdrant Configuration](/documentation/guides/configuration/#configurati ## Advanced Optimizations -You can change the *Optimzer CPU Budget* and the *Async Scorer* configurations for your cluster. These advanced settings will have an impact on performance and reliability. We recommend using the default values unless you are confident they are required for your use case. +Configuring these advanced settings will have an impact on performance and reliability. We recommend using the default values unless you are confident they are required for your use case. -See [Qdrant under the hood: io_uring](/articles/io_uring/#and-what-about-qdrant) and [Large Scale Search](/documentation/database-tutorials/large-scale-search/) for more details. +*Optimizer CPU Budget* + +Configures how many CPUs (threads) to allocate for optimization and indexing jobs: + +* If 0 or empty (default) - Qdrant keeps one or more CPU cores unallocated from optimization jobs, depending on the number of available CPUs, optimization jobs, and traffic load. +* If negative - Qdrant subtracts this number of CPUs from the available CPUs and uses them for optimizations +* If positive - Qdrant uses this exact number of CPUs for optimizations + +*Async Scorer* + +Enables async scorer which uses io_uring when rescoring. See [Qdrant under the hood: io_uring](/articles/io_uring/#and-what-about-qdrant) and [Large Scale Search](/documentation/database-tutorials/large-scale-search/) for more details. ## Client IP Restrictions @@ -44,3 +62,9 @@ Qdrant Cloud offers three strategies for shard rebalancing: * `by_size`: This strategy will rebalance the shards based on their size only. It will ensure that shards are evenly distributed across nodes by size, but the number of shards may not be even across all nodes. You can deactivate automatic shard rebalancing by deselecting the `rebalancing_strategy` option. This is useful if you want to manually control the shard distribution across nodes. + +## Rename a Cluster + +You can rename a Qdrant Cluster by clicking the pencil icon next to the cluster name on the Cluster Details page. + +Renaming a cluster does not affect its functionality or configuration. The cluster's unique ID and cluster URLs will remain the same. diff --git a/qdrant-landing/content/documentation/concepts/collections.md b/qdrant-landing/content/documentation/concepts/collections.md index 7a5bb3150..edf53f0d8 100644 --- a/qdrant-landing/content/documentation/concepts/collections.md +++ b/qdrant-landing/content/documentation/concepts/collections.md @@ -224,7 +224,6 @@ distributed and indexed. "result": { "status": "green", "optimizer_status": "ok", - "vectors_count": 1068786, "indexed_vectors_count": 1024232, "points_count": 1068786, "segments_count": 31, @@ -303,7 +302,6 @@ It is shown next to the grey collection status on the collection info page. You may be interested in the count attributes: - `points_count` - total number of objects (vectors and their payloads) stored in the collection -- `vectors_count` - total number of vectors in a collection, useful if you have multiple vectors per point - `indexed_vectors_count` - total number of vectors stored in the HNSW or sparse index. Qdrant does not store all the vectors in the index, but only if an index segment might be created for a given configuration. The above counts are not exact, but should be considered approximate. Depending @@ -318,7 +316,7 @@ reasons. Updates you do are therefore not directly reflected in these numbers. If you see a wildly different count of points, it will likely resolve itself once a new -round of automatic optimizations has completed. +round of automatic optimizations is completed. To clarify: these numbers don't represent the exact amount of points or vectors you have inserted, nor does it represent the exact number of distinguishable @@ -329,7 +327,7 @@ _Note: these numbers may be removed in a future version of Qdrant._ ### Indexing vectors in HNSW -In some cases, you might be surprised the value of `indexed_vectors_count` is lower than `vectors_count`. This is an intended behaviour and +In some cases, you might be surprised the value of `indexed_vectors_count` is lower than you expected. This is an intended behaviour and depends on the [optimizer configuration](/documentation/concepts/optimizer/). A new index segment is built if the size of non-indexed vectors is higher than the value of `indexing_threshold`(in kB). If your collection is very small or the dimensionality of the vectors is low, there might be no HNSW segment created and `indexed_vectors_count` might be equal to `0`. diff --git a/qdrant-landing/content/documentation/concepts/hybrid-queries.md b/qdrant-landing/content/documentation/concepts/hybrid-queries.md index 56a36058d..a128be545 100644 --- a/qdrant-landing/content/documentation/concepts/hybrid-queries.md +++ b/qdrant-landing/content/documentation/concepts/hybrid-queries.md @@ -192,6 +192,20 @@ In this case we use a **gauss_decay** function. {{< code-snippet path="/documentation/headless/snippets/query-points/score-boost-closer-to-user/" >}} +### Time-based score boosting + +Or combine the score with the information on how "fresh" the result is. It's applicable to (news) articles and in general many other different types of searches (think of the "newest" filter you use in applications). + +To implement time-based score boosting, you'll need each point to have a datetime field in its payload, e.g., when the item was uploaded or last updated. Then we can calculate the time difference in seconds between this payload value and the current time, our `target`. + +With an exponential decay function, perfect for use cases with time, as freshness is a very quickly lost quality, we can convert this time difference into a value between 0 and 1, then add it to the original score to prioritise fresh results. + +`score = score + exp_decay(current_time - point_time)` + +That's how it will look for an application where, after 1 day, results start being only half-relevant (so get a score of 0.5): + +{{< code-snippet path="/documentation/headless/snippets/query-points/score-boost-time/" >}} + For all decay functions, there are these parameters available | Parameter | Default | Description | diff --git a/qdrant-landing/content/documentation/concepts/indexing.md b/qdrant-landing/content/documentation/concepts/indexing.md index c6efad38d..8a117b4a7 100644 --- a/qdrant-landing/content/documentation/concepts/indexing.md +++ b/qdrant-landing/content/documentation/concepts/indexing.md @@ -245,9 +245,11 @@ storage: # Number of neighbours to consider during the index building. # Larger the value - more accurate the search, more time required to build index. ef_construct: 100 - # Minimal size (in KiloBytes) of vectors for additional payload-based indexing. - # If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used - - # in this case full-scan search should be preferred by query planner and additional indexing is not required. + # Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search. + # This measures the total size of vectors being queried against. + # When the maximum estimated amount of points that a condition satisfies is smaller than + # `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index + # traversal for better performance. # Note: 1Kb = 1 vector of size 256 full_scan_threshold: 10000 diff --git a/qdrant-landing/content/documentation/database-tutorials/migration.md b/qdrant-landing/content/documentation/database-tutorials/migration.md index 60b4e61ef..4fdfc12c6 100644 --- a/qdrant-landing/content/documentation/database-tutorials/migration.md +++ b/qdrant-landing/content/documentation/database-tutorials/migration.md @@ -41,16 +41,19 @@ Here is an example of how to perform a Qdrant to Qdrant migration: ```bash docker run --rm -it \ - -e SOURCE_API_KEY='your-source-key' \ - -e TARGET_API_KEY='your-target-key' \ registry.cloud.qdrant.io/library/qdrant-migration qdrant \ - --source-url 'https://source-instance.cloud.qdrant.io' \ - --source-collection 'benchmark' \ - --target-url 'https://target-instance.cloud.qdrant.io' \ - --target-collection 'benchmark' - + --source.url 'https://source-instance.cloud.qdrant.io:6334' \ + --source.api-key 'qdrant-source-key' \ + --source.collection 'benchmark' \ + --target.url 'https://target-instance.cloud.qdrant.io:6334' \ + --target.api-key 'qdrant-target-key' \ + --target.collection 'benchmark' ``` + + ## Example: Migrate from Pinecone to Qdrant Let’s now walk through an example of migrating from Pinecone to Qdrant. Assuming your Pinecone index looks like this: diff --git a/qdrant-landing/content/documentation/guides/common-errors.md b/qdrant-landing/content/documentation/guides/common-errors.md index b2a5b4367..52ba0e372 100644 --- a/qdrant-landing/content/documentation/guides/common-errors.md +++ b/qdrant-landing/content/documentation/guides/common-errors.md @@ -32,6 +32,69 @@ ulimit -n 10000 Please note, the command should be executed before you run Qdrant server. +## Incompatible file system + +Qdrant have a [set of requirements](https://qdrant.tech/documentation/guides/installation/#storage) for persistent file storage. +The most important requirement is that file system **must** be [POSIX-compatible](https://www.quobyte.com/storage-explained/posix-filesystem/). + + +Starting from v1.15.0 Qdrant performs runtime check of file system compatibility on start. +If it detects an unknown file system, you can see a warning like this: + +```text +WARN qdrant: There is a potential issue with +the filesystem for storage path ./storage. Details: +HFS/HFS+ filesystem support is untested +``` + +If runtime check fails, you might see an error message: + +```text +ERROR qdrant: Filesystem check failed for storage path ./storage. +Details: FUSE filesystems may cause data corruption due to caching issues +``` + +If an error like this is reported, it is NOT safe to continue working with current configuration and you're at risk of losing your data. + +Most common errors you might see, if you continue using Qdrant with incompatible file system: + +```text +ERROR +Panic occurred in file /qdrant/lib/gridstore/src/gridstore.rs at line 53: +called `Result::unwrap()` on an `Err` value: OutputTooSmall { expected: 4, actual: 0 } +``` + +or + +```text +ERROR +Service internal error: task XXX panicked with message +"called `Result::unwrap()` on an `Err` value: OutputTooSmall { expected: 4, actual: 0 }" +``` + +It might be also possible that vector data will be lost (set to all zeros) after service restart. + + +### How to avoid Incompatible file system? + +Most common used configuration of incompatible file system is usage of WSL-baced Docker containers in Windows. +When you mount Windows folder into Qdrant docker container, the Windows hyper visor creates a shared mount, which is not fully POSIX-compatible. + +Prefer to use docker volumes instead of bind mount: + +```bash +# Create named volume +docker volume create qdrant-storage + +# Use named volume with qdrant container +docker run --rm -it \ + -p 6333:6333 -p 6334:6334 \ + -v qdrant-storage:/qdrant/storage qdrant/qdrant:v1.15.3 +``` + +The above keeps the volume inside the Linux container, preventing issues with a mount shared with Windows. + + ## Can't open Collections meta Wal When starting a Qdrant instance as part of a distributed deployment, you may diff --git a/qdrant-landing/content/documentation/guides/configuration.md b/qdrant-landing/content/documentation/guides/configuration.md index ea4cfab4b..b879c92c1 100644 --- a/qdrant-landing/content/documentation/guides/configuration.md +++ b/qdrant-landing/content/documentation/guides/configuration.md @@ -138,6 +138,7 @@ log_level: INFO # log_level: INFO # # Logging format, supports `text` and `json` # format: text +# buffer_size_bytes: 1024 storage: # Where to store all the data @@ -163,7 +164,7 @@ storage: # It will be read from the disk every time it is requested. # This setting saves RAM by (slightly) increasing the response time. # Note: those payload values that are involved in filtering and are indexed - remain in RAM. - # + # # Default: true on_disk_payload: true @@ -190,11 +191,6 @@ storage: # Number of parallel threads used for search operations. If 0 - auto selection. max_search_threads: 0 - # Max number of threads (jobs) for running optimizations across all collections, each thread runs one job. - # If 0 - have no limit and choose dynamically to saturate CPU. - # Note: each optimization job will also use `max_indexing_threads` threads by itself for index building. - max_optimization_threads: 0 - # CPU budget, how many CPUs (threads) to allocate for an optimization job. # If 0 - auto selection, keep 1 or more CPUs unallocated depending on CPU size # If negative - subtract this number of CPUs from the available CPUs. @@ -251,20 +247,12 @@ storage: # If not set, will be automatically selected considering the number of available CPUs. max_segment_size_kb: null - # Maximum size (in KiloBytes) of vectors to store in-memory per segment. - # Segments larger than this threshold will be stored as read-only memmapped file. - # To enable memmap storage, lower the threshold - # Note: 1Kb = 1 vector of size 256 - # To explicitly disable mmap optimization, set to `0`. - # If not set, will be disabled by default. - memmap_threshold_kb: null - # Maximum size (in KiloBytes) of vectors allowed for plain index. - # Default value based on https://github.com/google-research/google-research/blob/master/scann/docs/algorithms.md + # Default value based on experiments and observations. # Note: 1Kb = 1 vector of size 256 # To explicitly disable vector indexing, set to `0`. # If not set, the default value will be used. - indexing_threshold_kb: 20000 + indexing_threshold_kb: 10000 # Interval between forced flushes. flush_interval_sec: 5 @@ -282,8 +270,7 @@ storage: # vacuum_min_vector_number: 1000 # default_segment_number: 0 # max_segment_size_kb: null - # memmap_threshold_kb: null - # indexing_threshold_kb: 20000 + # indexing_threshold_kb: 10000 # flush_interval_sec: 5 # max_optimization_threads: null @@ -295,9 +282,11 @@ storage: # Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index. ef_construct: 100 - # Minimal size (in KiloBytes) of vectors for additional payload-based indexing. - # If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used - - # in this case full-scan search should be preferred by query planner and additional indexing is not required. + # Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search. + # This measures the total size of vectors being queried against. + # When the maximum estimated amount of points that a condition satisfies is smaller than + # `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index + # traversal for better performance. # Note: 1Kb = 1 vector of size 256 full_scan_threshold_kb: 10000 @@ -364,6 +353,10 @@ storage: # Max oversampling value allowed in search. search_max_oversampling: null + # Maximum number of collections allowed to be created + # If null - no limit. + max_collections: null + service: # Maximum size of POST data in a single request in megabytes max_request_size_mb: 32 @@ -426,6 +419,8 @@ service: # Hardware reporting adds information to the API responses with a # hint on how many resources were used to execute the request. # + # Warning: experimental, this feature is still under development and is not supported yet. + # # Uncomment to enable. # hardware_reporting: true @@ -450,6 +445,12 @@ cluster: # We encourage you NOT to change this parameter unless you know what you are doing. tick_period_ms: 100 + # Compact consensus operations once we have this amount of applied + # operations. Allows peers to join quickly with a consensus snapshot without + # replaying a huge amount of operations. + # If 0 - disable compaction + compact_wal_entries: 128 + # Set to true to prevent service from sending usage statistics to the developers. # Read more: https://qdrant.tech/documentation/guides/telemetry telemetry_disabled: false diff --git a/qdrant-landing/content/documentation/guides/distributed_deployment.md b/qdrant-landing/content/documentation/guides/distributed_deployment.md index 30e263826..e12f1da7c 100644 --- a/qdrant-landing/content/documentation/guides/distributed_deployment.md +++ b/qdrant-landing/content/documentation/guides/distributed_deployment.md @@ -381,7 +381,6 @@ client.create_collection( sharding_method=models.ShardingMethod.CUSTOM, # ... other collection parameters ) -client.create_shard_key("{collection_name}", "{shard_key}") ``` ```typescript @@ -394,16 +393,11 @@ client.createCollection("{collection_name}", { sharding_method: "custom", // ... other collection parameters }); - -client.createShardKey("{collection_name}", { - shard_key: "{shard_key}" -}); ``` ```rust use qdrant_client::qdrant::{ - CreateCollectionBuilder, CreateShardKeyBuilder, CreateShardKeyRequestBuilder, Distance, - ShardingMethod, VectorParamsBuilder, + CreateCollectionBuilder, Distance, ShardingMethod, VectorParamsBuilder, }; use qdrant_client::Qdrant; @@ -417,13 +411,6 @@ client .sharding_method(ShardingMethod::Custom.into()), ) .await?; - -client - .create_shard_key( - CreateShardKeyRequestBuilder::new("{collection_name}") - .request(CreateShardKeyBuilder::default().shard_key("{shard_key".to_string())), - ) - .await?; ``` ```java @@ -433,8 +420,6 @@ import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; import io.qdrant.client.grpc.Collections.CreateCollection; import io.qdrant.client.grpc.Collections.ShardingMethod; -import io.qdrant.client.grpc.Collections.CreateShardKey; -import io.qdrant.client.grpc.Collections.CreateShardKeyRequest; QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build()); @@ -448,13 +433,6 @@ client .setShardingMethod(ShardingMethod.Custom) .build()) .get(); - -client.createShardKeyAsync(CreateShardKeyRequest.newBuilder() - .setCollectionName("{collection_name}") - .setRequest(CreateShardKey.newBuilder() - .setShardKey(shardKey("{shard_key}")) - .build()) - .build()).get(); ``` ```csharp @@ -469,11 +447,6 @@ await client.CreateCollectionAsync( shardNumber: 1, shardingMethod: ShardingMethod.Custom ); - -await client.CreateShardKeyAsync( - "{collection_name}", - new CreateShardKey { ShardKey = new ShardKey { Keyword = "{shard_key}", } } - ); ``` ```go @@ -494,10 +467,6 @@ client.CreateCollection(context.Background(), &qdrant.CreateCollection{ ShardNumber: qdrant.PtrOf(uint32(1)), ShardingMethod: qdrant.ShardingMethod_Custom.Enum(), }) - -client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{ - ShardKey: qdrant.NewShardKey("{shard_key}"), -}) ``` In this mode, the `shard_number` means the number of shards per shard key, where points will be distributed evenly. For example, if you have 10 shard keys and a collection config with these settings: @@ -516,6 +485,97 @@ Physical shards require a large amount of resources, so make sure your custom sh For large cardinality keys, it is recommended to use [partition by payload](/documentation/guides/multiple-partitions/#partition-by-payload) instead. +Now you need to create custom shards ([API reference](https://api.qdrant.tech/api-reference/distributed/create-shard-key#request)): + +```http +PUT /collections/{collection_name}/shards +{ + "shard_key": "{shard_key}" +} +``` + +```python +from qdrant_client import QdrantClient, models + +client = QdrantClient(url="http://localhost:6333") + +client.create_shard_key("{collection_name}", "{shard_key}") +``` + +```typescript +import { QdrantClient } from "@qdrant/js-client-rest"; + +const client = new QdrantClient({ host: "localhost", port: 6333 }); + +client.createShardKey("{collection_name}", { + shard_key: "{shard_key}" +}); +``` + +```rust +use qdrant_client::qdrant::{ + CreateShardKeyBuilder, CreateShardKeyRequestBuilder +}; +use qdrant_client::Qdrant; + +let client = Qdrant::from_url("http://localhost:6334").build()?; + +client + .create_shard_key( + CreateShardKeyRequestBuilder::new("{collection_name}") + .request(CreateShardKeyBuilder::default().shard_key("{shard_key".to_string())), + ) + .await?; +``` + +```java +import static io.qdrant.client.ShardKeyFactory.shardKey; + +import io.qdrant.client.QdrantClient; +import io.qdrant.client.QdrantGrpcClient; +import io.qdrant.client.grpc.Collections.CreateShardKey; +import io.qdrant.client.grpc.Collections.CreateShardKeyRequest; + +QdrantClient client = + new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build()); + +client.createShardKeyAsync(CreateShardKeyRequest.newBuilder() + .setCollectionName("{collection_name}") + .setRequest(CreateShardKey.newBuilder() + .setShardKey(shardKey("{shard_key}")) + .build()) + .build()).get(); +``` + +```csharp +using Qdrant.Client; +using Qdrant.Client.Grpc; + +var client = new QdrantClient("localhost", 6334); + +await client.CreateShardKeyAsync( + "{collection_name}", + new CreateShardKey { ShardKey = new ShardKey { Keyword = "{shard_key}", } } + ); +``` + +```go +import ( + "context" + + "github.com/qdrant/go-client/qdrant" +) + +client, err := qdrant.NewClient(&qdrant.Config{ + Host: "localhost", + Port: 6334, +}) + +client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{ + ShardKey: qdrant.NewShardKey("{shard_key}"), +}) +``` + To specify the shard for each point, you need to provide the `shard_key` field in the upsert request: ```http @@ -549,6 +609,10 @@ client.upsert( ``` ```typescript +import { QdrantClient } from "@qdrant/js-client-rest"; + +const client = new QdrantClient({ host: "localhost", port: 6333 }); + client.upsert("{collection_name}", { points: [ { diff --git a/qdrant-landing/content/documentation/guides/installation.md b/qdrant-landing/content/documentation/guides/installation.md index d6f5a5d74..925e18877 100644 --- a/qdrant-landing/content/documentation/guides/installation.md +++ b/qdrant-landing/content/documentation/guides/installation.md @@ -40,6 +40,8 @@ Qdrant won't work with [Network file systems](https://en.wikipedia.org/wiki/File If you offload vectors to a local disk, we recommend you use a solid-state (SSD or NVMe) drive. + + ### Networking Each Qdrant instance requires three open ports: @@ -210,7 +212,7 @@ configs: log_level: INFO ``` - + ### From source diff --git a/qdrant-landing/content/documentation/guides/multiple-partitions.md b/qdrant-landing/content/documentation/guides/multiple-partitions.md index c1e74c1a4..d38dddd3c 100644 --- a/qdrant-landing/content/documentation/guides/multiple-partitions.md +++ b/qdrant-landing/content/documentation/guides/multiple-partitions.md @@ -7,7 +7,11 @@ aliases: --- # Configure Multitenancy -**How many collections should you create?** In most cases, you should only use a single collection with payload-based partitioning. This approach is called multitenancy. It is efficient for most of users, but it requires additional configuration. This document will show you how to set it up. + + +**How many collections should you create?** In most cases, a single collection per embedding model with payload-based partitioning for different tenants and use cases. This approach is called multitenancy. It is efficient for most users, but requires additional configuration. This document will show you how to set it up. **When should you create multiple collections?** When you have a limited number of users and you need isolation. This approach is flexible, but it may be more costly, since creating numerous collections may result in resource overhead. Also, you need to ensure that they do not affect each other in any way, including performance-wise. diff --git a/qdrant-landing/content/documentation/guides/quantization.md b/qdrant-landing/content/documentation/guides/quantization.md index 368988d11..25943c0c2 100644 --- a/qdrant-landing/content/documentation/guides/quantization.md +++ b/qdrant-landing/content/documentation/guides/quantization.md @@ -252,6 +252,13 @@ In this case, the quantized vector will be 16 times smaller than the original ve `always_ram` - whether to keep quantized vectors always cached in RAM or not. By default, quantized vectors are loaded in the same way as the original vectors. However, in some setups you might want to keep quantized vectors in RAM to speed up the search process. Then set `always_ram` to `true`. + +### Disabling Quantization + +To disable quantization in an existing collection, you can do the following: + +{{< code-snippet path="/documentation/headless/snippets/update-collection/disable-quantization/" >}} + ### Searching with Quantization Once you have configured quantization for a collection, you don't need to do anything extra to search with quantization. diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-tags/http.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-tags/http.md index 019756dcf..46bf8285e 100644 --- a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-tags/http.md +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-tags/http.md @@ -8,13 +8,14 @@ POST /collections/{collection_name}/points/query "query": { "formula": { "sum": [ - "$score, + "$score", { "mult": [ 0.5, { "key": "tag", - "match": { "any": ["h1", "h2", "h3", "h4"] } } + "match": { "any": ["h1", "h2", "h3", "h4"] } + } ] }, { diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/_description.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/_description.md new file mode 100644 index 000000000..9fd5b3b0f --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/_description.md @@ -0,0 +1 @@ +This code snippet applies exponential decay to boost the relevance of search results based on a datetime field called "upload_time" in the payload (which can be when a point was uploaded to Qdrant). Items closer in time to a specified `target`, so, in our case, the current datetime, which means "fresher" results, receive higher scores. Relevance score is decreasing exponentially with "upload_time" getting further away from `target` time, reaching a 0.5 `midpoint` of relevance after a defined `scale` period of 1 day. \ No newline at end of file diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/csharp.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/csharp.md new file mode 100644 index 000000000..a6edbf7cd --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/csharp.md @@ -0,0 +1,33 @@ +```csharp +using Qdrant.Client; +using Qdrant.Client.Grpc; + +var client = new QdrantClient("localhost", 6334); + +await client.QueryAsync( + collectionName: "{collection_name}", + prefetch: + [ + new PrefetchQuery { Query = new float[] { 0.2f, 0.8f, ..., .. }, Limit = 50 }, // <-- dense vector + ], + query: new Formula + { + Expression = new SumExpression + { + Sum = // the final score = score + exp_decay(target_time - x_time) + { + "$score", + Expression.FromExpDecay( + new() + { + X = Expression.FromDateTimeKey("update_time"), // payload key + Target = Expression.FromDateTime("YYYY-MM-DDT00:00:00Z"), // current datetime + Midpoint = 0.5f, + Scale = 86400 // 1 day in seconds + } + ) + } + } + } +); +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/go.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/go.md new file mode 100644 index 000000000..42d6a53bf --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/go.md @@ -0,0 +1,35 @@ +```go +import ( + "context" + + "github.com/qdrant/go-client/qdrant" +) + +client, err := qdrant.NewClient(&qdrant.Config{ + Host: "localhost", + Port: 6334, +}) + +client.Query(context.Background(), &qdrant.QueryPoints{ + CollectionName: "{collection_name}", + Prefetch: []*qdrant.PrefetchQuery{ + { + Query: qdrant.NewQuery(0.2, 0.8, .., ...), // <-- dense vector + Limit: qdrant.PtrOf(uint64(50)), + }, + }, + Query: qdrant.NewQueryFormula(&qdrant.Formula{ + Expression: qdrant.NewExpressionSum(&qdrant.SumExpression{ + Sum: []*qdrant.Expression{ // the final score = score + exp_decay(target_time - x_time) + qdrant.NewExpressionVariable("$score"), + qdrant.NewExpressionExpDecay(&qdrant.DecayParamsExpression{ + X: qdrant.NewExpressionDatetimeKey("update_time"), // payload key + Target: qdrant.NewExpressionDatetime("YYYY-MM-DDT00:00:00Z"), // current datetime + Scale: qdrant.PtrOf(float32(86400)), // 1 day in seconds + Midpoint: qdrant.PtrOf(float32(0.5)), + }), + }, + }), + }), +}) +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/http.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/http.md new file mode 100644 index 000000000..c83379190 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/http.md @@ -0,0 +1,28 @@ +```http +POST /collections/{collection_name}/points/query +{ + "prefetch": { + "query": [0.2, 0.8, ...], // <-- dense vector + "limit": 50 + }, + "query": { + "formula": { + "sum": [ + "$score", // the final score = score + exp_decay(target_time - x_time) + { + "exp_decay": { + "x": { + "datetime_key": "update_time" // payload key + }, + "target": { + "datetime": "YYYY-MM-DDT00:00:00Z" // current datetime + }, + "scale": 86400, // 1 day in seconds + "midpoint": 0.5 // if item's "update_time" is more than 1 day apart from current datetime, relevance score is less than 0.5 + } + } + ] + } + } +} +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/java.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/java.md new file mode 100644 index 000000000..6a0cd865f --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/java.md @@ -0,0 +1,52 @@ +```java +import io.qdrant.client.QdrantClient; +import io.qdrant.client.QdrantGrpcClient; +import io.qdrant.client.grpc.Points.DecayParamsExpression; +import io.qdrant.client.grpc.Points.Formula; +import io.qdrant.client.grpc.Points.PrefetchQuery; +import io.qdrant.client.grpc.Points.QueryPoints; +import io.qdrant.client.grpc.Points.ScoredPoint; +import io.qdrant.client.grpc.Points.SumExpression; +import java.util.List; + +import static io.qdrant.client.ExpressionFactory.datetime; +import static io.qdrant.client.ExpressionFactory.datetimeKey; +import static io.qdrant.client.ExpressionFactory.expDecay; +import static io.qdrant.client.ExpressionFactory.sum; +import static io.qdrant.client.ExpressionFactory.variable; +import static io.qdrant.client.QueryFactory.formula; +import static io.qdrant.client.QueryFactory.nearest; + +QdrantClient client = + new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build()); + +List time_boosted = client.queryAsync( + QueryPoints.newBuilder() + .setCollectionName({collection_name}) + .addPrefetch( + PrefetchQuery.newBuilder() + .setQuery(nearest(0.2f, 0.8f, .., ..)) // <-- dense vector + .setLimit(50) + .build()) + .setQuery( + formula( + Formula.newBuilder() + .setExpression( + sum( // the final score = score + exp_decay(target_time - x_time) + SumExpression.newBuilder() + .addSum(variable("$score")) + .addSum( + expDecay( + DecayParamsExpression.newBuilder() + .setX( + datetimeKey("update_time")) // payload key + .setTarget( + datetime("YYYY-MM-DDT00:00:00Z")) // current datetime + .setMidpoint(0.5f) + .setScale(86400) // 1 day in seconds + .build())) + .build())) + .build())) + .build() +).get(); +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/python.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/python.md new file mode 100644 index 000000000..ccb689e44 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/python.md @@ -0,0 +1,31 @@ +```python +from qdrant_client import models + + +time_boosted = client.query_points( + collection_name="{collection_name}", + prefetch=models.Prefetch( + query=[0.2, 0.8, ...], # <-- dense vector + limit=50 + ), + query=models.FormulaQuery( + formula=models.SumExpression( + sum=[ + "$score", # the final score = score + exp_decay(target_time - x_time) + models.ExpDecayExpression( + exp_decay=models.DecayParamsExpression( + x=models.DatetimeKeyExpression( + datetime_key="upload_time" # payload key + ), + target=models.DatetimeExpression( + datetime="YYYY-MM-DDT00:00:00Z" # current datetime + ), + scale=86400, # 1 day in seconds + midpoint=0.5 # if item's "update_time" is more than 1 day apart from current datetime, relevance score is less than 0.5 + ) + ) + ] + ) + ) +) +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/rust.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/rust.md new file mode 100644 index 000000000..23fcc767c --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/rust.md @@ -0,0 +1,29 @@ +```rust +use qdrant_client::qdrant::{ + DecayParamsExpressionBuilder, Expression, FormulaBuilder, PrefetchQueryBuilder, QueryPointsBuilder, +}; +use qdrant_client::Qdrant; + +let client = Qdrant::from_url("http://localhost:6334").build()?; + +let _geo_boosted = client.query( + QueryPointsBuilder::new("{collection_name}") + .add_prefetch( + PrefetchQueryBuilder::default() + .query(vec![0.2, 0.8, .., ..]) // <-- dense vector + .limit(50u64), + ) + .query( + FormulaBuilder::new(Expression::sum_with([ // the final score = score + exp_decay(target_time - x_time) + Expression::score(), + Expression::exp_decay( + DecayParamsExpressionBuilder::new(Expression::datetime_key("update_time")) // payload key + .target(Expression::datetime("YYYY-MM-DDT00:00:00Z")) + .midpoint(0.5) + .scale(86400.0), // 1 day in seconds + ), + ])) + ) + ) + .await?; +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/typescript.md b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/typescript.md new file mode 100644 index 000000000..b08705dc3 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/query-points/score-boost-time/typescript.md @@ -0,0 +1,31 @@ +```typescript +import { QdrantClient } from "@qdrant/js-client-rest"; + +const client = new QdrantClient({ host: "localhost", port: 6333 }); + +const time_boosted = await client.query(collectionName, { + prefetch: { + query: [0.2, 0.8, ...], // <-- dense vector + limit: 50 + }, + query: { + formula: { + sum: [ // the final score = score + exp_decay(target_time - x_time) + "$score", + { + exp_decay: { + x: { + datetime_key: "update_time" // payload key + }, + target: { + datetime: "YYYY-MM-DDT00:00:00Z" // current datetime + }, + midpoint: 0.5, + scale: 86400 // 1 day in seconds + } + } + ] + } + } +}); +``` \ No newline at end of file diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/_description.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/_description.md new file mode 100644 index 000000000..4e693a034 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/_description.md @@ -0,0 +1 @@ +This code snippet demonstrates disabling quantization for a collection. diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/bash.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/bash.md new file mode 100644 index 000000000..769a7fc4f --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/bash.md @@ -0,0 +1,7 @@ +```bash +curl -X PATCH http://localhost:6333/collections/{collection_name} \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "quantization_config": "Disabled" + }' +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/csharp.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/csharp.md new file mode 100644 index 000000000..e9a40c9d9 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/csharp.md @@ -0,0 +1,11 @@ +```csharp +using Qdrant.Client; +using Qdrant.Client.Grpc; + +var client = new QdrantClient("localhost", 6334); + +await client.UpdateCollectionAsync( + collectionName: "{collection_name}", + quantizationConfig: new QuantizationConfigDiff { Disabled = new Disabled() } +); +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/go.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/go.md new file mode 100644 index 000000000..e5c93b11f --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/go.md @@ -0,0 +1,17 @@ +```go +import ( + "context" + + "github.com/qdrant/go-client/qdrant" +) + +client, err := qdrant.NewClient(&qdrant.Config{ + Host: "localhost", + Port: 6334, +}) + +client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{ + CollectionName: "{collection_name}", + QuantizationConfig: qdrant.NewQuantizationDiffDisabled(), +}) +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/http.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/http.md new file mode 100644 index 000000000..10782c96e --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/http.md @@ -0,0 +1,6 @@ +```http +PATCH /collections/{collection_name} +{ + "quantization_config": "Disabled" +} +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/java.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/java.md new file mode 100644 index 000000000..deb41c7ec --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/java.md @@ -0,0 +1,14 @@ +```java +import io.qdrant.client.grpc.Collections.Disabled; +import io.qdrant.client.grpc.Collections.QuantizationConfigDiff; +import io.qdrant.client.grpc.Collections.UpdateCollection; + +client.updateCollectionAsync( + UpdateCollection.newBuilder() + .setCollectionName("{collection_name}") + .setQuantizationConfig( + QuantizationConfigDiff.newBuilder() + .setDisabled(Disabled.getDefaultInstance()) + .build()) + .build()); +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/python.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/python.md new file mode 100644 index 000000000..7b9985e17 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/python.md @@ -0,0 +1,6 @@ +```python +client.update_collection( + collection_name="{collection_name}", + quantization_config=models.Disabled.DISABLED, +) +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/rust.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/rust.md new file mode 100644 index 000000000..ba9aaff59 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/rust.md @@ -0,0 +1,7 @@ +```rust +use qdrant_client::qdrant::{Disabled, UpdateCollectionBuilder}; + +client + .update_collection(UpdateCollectionBuilder::new("{collection_name}").quantization_config(Disabled {})) + .await?; +``` diff --git a/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/typescript.md b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/typescript.md new file mode 100644 index 000000000..01e134730 --- /dev/null +++ b/qdrant-landing/content/documentation/headless/snippets/update-collection/disable-quantization/typescript.md @@ -0,0 +1,5 @@ +```typescript +client.updateCollection("{collection_name}", { + quantization_config: 'Disabled' +}); +``` diff --git a/qdrant-landing/content/documentation/private-cloud/_index.md b/qdrant-landing/content/documentation/private-cloud/_index.md index 631af89c7..d6f57a8be 100644 --- a/qdrant-landing/content/documentation/private-cloud/_index.md +++ b/qdrant-landing/content/documentation/private-cloud/_index.md @@ -18,4 +18,12 @@ On top of the open source Qdrant database, it allows * Extended telemetry * Qdrant Enterprise Support Services +Since there is no communication or connection with Qdrant, you are fully responsible for the entire security of the Qdrant Private Cloud installation. This also means that you do not benefit from all the integrated management and observability features of Qdrant Managed Cloud and Hybrid Cloud, such as: + +* A central management UI and API +* Integrated monitoring and alerting +* Configuration recommendations + + We encourage you to weigh your security requirements against Hybrid Cloud before inquiring about Private Cloud. + If you are interested in using Qdrant Private Cloud, please [contact us](/contact-us/) for more information. diff --git a/qdrant-landing/content/documentation/private-cloud/configuration.md b/qdrant-landing/content/documentation/private-cloud/configuration.md index 04bfefb48..81ffc43d1 100644 --- a/qdrant-landing/content/documentation/private-cloud/configuration.md +++ b/qdrant-landing/content/documentation/private-cloud/configuration.md @@ -359,4 +359,139 @@ qdrant-cluster-manager: tolerations: [] affinity: {} + +qdrant-cluster-exporter: + image: + repository: registry.cloud.qdrant.io/qdrant/qdrant-cluster-exporter + pullPolicy: Always + # Overrides the image tag. Defaults to the chart appVersion. + tag: "" + + imagePullSecrets: + - name: qdrant-registry-creds + + nameOverride: "" + fullnameOverride: "" + + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + + rbac: + create: true + + podAnnotations: {} + + podSecurityContext: + runAsNonRoot: true + runAsUser: 65534 + runAsGroup: 65534 + fsGroup: 65534 + + securityContext: + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 65534 + runAsGroup: 65534 + + service: + enabled: true + type: ClusterIP + port: 9090 + portName: metrics + + strategy: + # Prevents double-scraping by terminating the old pod before creating a new one + # The pod scrapes a large volume of metrics with high cardinality + type: Recreate + + resources: {} + # We usually recommend not setting default resources and to leave this as a conscious + # choice for the user. This allows charts to run on environments with fewer + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + + nodeSelector: {} + + tolerations: [] + + affinity: {} + + serviceMonitor: + enabled: true + honorLabels: true + scrapeInterval: 60s + scrapeTimeout: 55s + + # Limit RBAC to the release namespace + limitRBAC: false + + # Watched Namespaces Configuration + watch: + # If true, only the namespace where the exporter is deployed is watched, otherwise it watches the namespaces defined in watch.namespaces + onlyReleaseNamespace: false + # an empty list watches all namespaces + namespaces: [] + + # Configuration for the qdrant cluster exporter + config: + # The log level for the cluster-exporter + # Available options: DEBUG | INFO | WARN | ERROR + logLevel: INFO + # Controller related settings + controller: + # Schedule for the controller to do a forced resync (if watches are missed / nothing happened) + forceResyncPeriod: 10h + # Indicates the maximum QPS from this client to the master + # Default is 200 + qps: 200 + # Maximum burst for throttle. + # Default is 500. + burst: 500 + # Maximum number of concurrent reconciliations + maxConcurrentReconciles: 20 + # Controller's object requeueing interval + requeueInterval: 30s + # Exporter Metrics Configuration + metrics: + # The port on which the metrics are exposed + port: 9090 + # The path on which the metrics are exposed + path: /metrics + # Exporter Health Check Configuration + healthz: + # The port used for the health probe + port: 8085 + # Qdrant Telemetry and Metrics Cache Configuration + cache: + # The period after which the cache is invalidated + ttl: 60s + # Qdrant Rest Client Configuration + qdrant: + restAPI: + # The qdrant rest api port + port: 6333 + # Qdrant API Request Timeout after which requests to Qdrant are canceled if not completed + timeout: 20s + # Path where qdrant exposes metrics + metricsPath: "metrics" + # Qdrant Telemetry Configuration + telemetry: + # Path where qdrant exposes telemetry + path: "telemetry" + # The level of details for telemetry + detailsLevel: 6 + # Whether to anonymize the telemetry data + anonymize: true ``` \ No newline at end of file diff --git a/qdrant-landing/content/documentation/private-cloud/private-cloud-setup.md b/qdrant-landing/content/documentation/private-cloud/private-cloud-setup.md index ee7120cfa..feabacf9e 100644 --- a/qdrant-landing/content/documentation/private-cloud/private-cloud-setup.md +++ b/qdrant-landing/content/documentation/private-cloud/private-cloud-setup.md @@ -33,12 +33,104 @@ Container images: - `registry.cloud.qdrant.io/qdrant/qdrant` - `registry.cloud.qdrant.io/qdrant/operator` - `registry.cloud.qdrant.io/qdrant/cluster-manager` +- `registry.cloud.qdrant.io/qdrant/qdrant-cluster-exporter` Open Containers Initiative (OCI) Helm charts: - `registry.cloud.qdrant.io/qdrant-charts/qdrant-private-cloud` - `registry.cloud.qdrant.io/library/qdrant-kubernetes-api` +- The specific versions for every private cloud version are documented in the [Private Cloud Changelog](/documentation/private-cloud/changelog/). + +## Installation + +Once onboarded to Qdrant Private Cloud, you will receive credentials to access the Qdrant Cloud Registry. You can use these credentials to install the Qdrant Private Cloud solution using the following commands: + +1. Create the namespace for your Private Cloud deployment. You can use any name for the namespace, but you will need to update the later steps to reflect this. E.g. + +```bash +kubectl create namespace qdrant-private-cloud +``` + +2. Create a Kubernetes secret with your Qdrant Cloud Registry credentials, to allow your Kubernetes cluster to pull the necessary container images: +``` +kubectl create secret docker-registry qdrant-registry-creds --docker-server=registry.cloud.qdrant.io --docker-username='your-username' --docker-password='your-password' --namespace qdrant-private-cloud +``` + +3. Log in to the Qdrant Cloud Registry using Helm: + +```bash +helm registry login 'registry.cloud.qdrant.io' --username 'your-username' --password 'your-password' +``` + +4. Install the Qdrant Kubernetes Operator Custom Resource Definitions (CRDs): + +```bash +helm upgrade --install qdrant-private-cloud-crds oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-kubernetes-api --namespace qdrant-private-cloud --version v1.17.2 --wait +``` + +5. Install Qdrant Private Cloud: + +```bash +helm upgrade --install qdrant-private-cloud oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-private-cloud --namespace qdrant-private-cloud --version 1.8.0 +``` + +Ensure that the `qdrant-kubernetes-api` version is compatible with the `qdrant-private-cloud` version you are installing. + +For a list of available versions consult the [Private Cloud Changelog](/documentation/private-cloud/changelog/). + +Current default versions are: + +* qdrant-kubernetes-api v1.17.2 +* qdrant-private-cloud 1.8.0 + +For more information also see the [Helm Install Documentation](https://helm.sh/docs/helm/helm_install/). + +## Configuring Private Cloud + +The Qdrant Private Cloud Helm chart comes with a set of default values which are suitable for most deployments. However, you are able to customize the configuration further to fit your specific needs. See the [Private Cloud Configuration](/documentation/private-cloud/configuration/) page for all available configuration options. + +You must ensure that the default `StorageClasses` and corresponding `VolumeSnapshotClass` are set appropriately for your environment. + +When creating your own `values.yaml` file, as a best practice, only include the values you are actually changing, e.g. with this `values.yaml` file: + +```yaml +operator: + settings: + features: + clusterManagement: + storageClass: + database: your-storage-class-name + snapshot: your-storage-class-name + backupManagement: + snapshots: + volumeSnapshotClass: your-volume-snapshot-class-name +``` + +You can configure Qdrant Private Cloud like this: + +```bash +helm upgrade --install qdrant-private-cloud oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-private-cloud --namespace qdrant-private-cloud --version 1.8.0 -f values.yaml +``` + +## Upgrades + +To upgrade Qdrant Private Cloud to a new version, first upgrade the Qdrant Kubernetes Operator Custom Resource Definitions (CRDs): + +```bash +helm upgrade --install qdrant-private-cloud-crds oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-kubernetes-api --namespace qdrant-private-cloud --version v1.17.2 --wait +``` + +Then upgrade the Qdrant Private Cloud Helm chart using the same configuration values, e.g.: + +```bash +helm upgrade --install qdrant-private-cloud oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-private-cloud --namespace qdrant-private-cloud --version 1.8.0 -f values.yaml +``` + +Note, that the image tag values are automatically derived from the chart's appVersions and should not be overridden in the `values.yaml`. + +For more information also see the [Helm Upgrade Documentation](https://helm.sh/docs/helm/upgrade/). + ### Mirroring images and charts To mirror all necessary container images and Helm charts into your own registry, you can either use a replication feature that your registry provides, or you can manually sync the images with [Skopeo](https://github.com/containers/skopeo): @@ -70,28 +162,36 @@ skopeo sync --all --src docker --dest docker registry.cloud.qdrant.io/qdrant-cha skopeo sync --all --src docker --dest docker registry.cloud.qdrant.io/qdrant-charts/qdrant-kubernetes-api your-registry.example.com/qdrant-charts/qdrant-kubernetes-api ``` -During the installation or upgrade, you will need to adapt the repository information in the Helm chart values. See [Private Cloud Configuration](/documentation/private-cloud/configuration/) for details. +During the installation or upgrade, you will need to adapt the image repository and imagePullSecret information in the Helm chart values, e.g.: -## Installation and Upgrades - -Once you are onboarded to Qdrant Private Cloud, you will receive credentials to access the Qdrant Cloud Registry. You can use these credentials to install the Qdrant Private Cloud solution using the following commands. You can choose the Kubernetes namespace freely. - -```bash -kubectl create namespace qdrant-private-cloud -kubectl create secret docker-registry qdrant-registry-creds --docker-server=registry.cloud.qdrant.io --docker-username='your-username' --docker-password='your-password' --namespace qdrant-private-cloud -helm registry login 'registry.cloud.qdrant.io' --username 'your-username' --password 'your-password' -helm upgrade --install qdrant-private-cloud-crds oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-kubernetes-api --namespace qdrant-private-cloud --version v1.17.2 --wait -helm upgrade --install qdrant-private-cloud oci://registry.cloud.qdrant.io/qdrant-charts/qdrant-private-cloud --namespace qdrant-private-cloud --version 1.8.0 +```yaml +operator: + image: + repository: your-registry.example.com/qdrant/operator + imagePullSecrets: + - name: your-registry-creds + settings: + features: + clusterManagement: + qdrant: + image: + repository: your-registry.example.com/qdrant/qdrant + pullSecretName: your-registry-creds + +qdrant-cluster-manager: + image: + repository: your-registry.example.com/qdrant/cluster-manager + imagePullSecrets: + - name: your-registry-creds + +qdrant-cluster-exporter: + image: + repository: your-registry.example.com/qdrant/qdrant-cluster-exporter + imagePullSecrets: + - name: your-registry-creds ``` -For a list of available versions consult the [Private Cloud Changelog](/documentation/private-cloud/changelog/). - -Current default versions are: - -* qdrant-kubernetes-api v1.17.2 -* qdrant-private-cloud 1.8.0 - -Especially ensure, that the default values to reference `StorageClasses` and the corresponding `VolumeSnapshotClass` are set correctly in your environment. +See [Private Cloud Configuration](/documentation/private-cloud/configuration/) for details. ### Scope of the operator diff --git a/qdrant-landing/content/headless/customer-list.md b/qdrant-landing/content/headless/customer-list.md index 77167b7ea..134050254 100644 --- a/qdrant-landing/content/headless/customer-list.md +++ b/qdrant-landing/content/headless/customer-list.md @@ -45,10 +45,10 @@ customers: - id: 14 name: Kaufland logo: -- id: 16 +- id: 15 name: Deloitte logo: -- id: 17 +- id: 16 name: Hewlett-Packard-Enterprise logo: sitemapExclude: true diff --git a/qdrant-landing/content/headless/docs-header.md b/qdrant-landing/content/headless/docs-header.md index 626560b2e..d06d4b735 100644 --- a/qdrant-landing/content/headless/docs-header.md +++ b/qdrant-landing/content/headless/docs-header.md @@ -28,5 +28,10 @@ menuItems: url: https://api.qdrant.tech/api-reference external: true icon: roadmap-white.svg + # - id: menu-5 + # name: Course + # url: /course/ + # external: true +# icon: roadmap-white.svg sitemapExclude: true --- diff --git a/qdrant-landing/content/headless/stats.md b/qdrant-landing/content/headless/stats.md index 0e53ca7e6..b3fa8fc1c 100644 --- a/qdrant-landing/content/headless/stats.md +++ b/qdrant-landing/content/headless/stats.md @@ -1,6 +1,6 @@ --- stats: - githubStars: 25.1k - discordMembers: 8.3k + githubStars: 25.9k + discordMembers: 8.6k twitterFollowers: 7.5k --- \ No newline at end of file diff --git a/qdrant-landing/content/headless/top-banner.md b/qdrant-landing/content/headless/top-banner.md index 3a65b5d41..4a52944e5 100644 --- a/qdrant-landing/content/headless/top-banner.md +++ b/qdrant-landing/content/headless/top-banner.md @@ -10,11 +10,11 @@ icon: -text: "Natively embed text, image, and sparse vectors with Qdrant Cloud Inference. Now live." +text: "Think Outside the Bot Hackathon | Over $10k in prizes | Submit project by Sept 16" link: text: Learn more - url: https://qdrant.tech/blog/qdrant-cloud-inference-launch/ -start: 2025-07-15T05:00:00.000Z + url: https://try.qdrant.tech/hackathon-2025 +start: 2025-08-13T05:00:00.000Z sitemapExclude: true -end: 2025-07-31T14:00:00.000Z +end: 2025-09-15T14:00:00.000Z --- diff --git a/qdrant-landing/content/stars/stars-get-started.md b/qdrant-landing/content/stars/stars-get-started.md index f6f25347c..c09b03ae8 100644 --- a/qdrant-landing/content/stars/stars-get-started.md +++ b/qdrant-landing/content/stars/stars-get-started.md @@ -1,7 +1,7 @@ --- title: Are you contributing to our code, content, or community? button: - url: https://forms.gle/q4fkwudDsy16xAZk8 + url: https://forms.gle/vTuy8Fe9RFdt4SiB9 text: Become a Star image: src: /img/stars.svg diff --git a/qdrant-landing/content/stars/stars-hero.md b/qdrant-landing/content/stars/stars-hero.md index 1fa079d67..47f14aac1 100644 --- a/qdrant-landing/content/stars/stars-hero.md +++ b/qdrant-landing/content/stars/stars-hero.md @@ -3,7 +3,7 @@ title: Qdrant Stars description: A program for developers building, sharing, and leading in the Qdrant community.

Qdrant Stars recognizes and supports our most active contributors. If you’re helping others get more out of Qdrant, this program is for you. button: text: Become a Star - url: https://forms.gle/q4fkwudDsy16xAZk8 + url: https://forms.gle/vTuy8Fe9RFdt4SiB9 image: src: /img/stars-hero.svg alt: Stars diff --git a/qdrant-landing/package-lock.json b/qdrant-landing/package-lock.json index 3eed866dc..5fadde868 100644 --- a/qdrant-landing/package-lock.json +++ b/qdrant-landing/package-lock.json @@ -12,7 +12,7 @@ "anchor-js": "^5.0.0", "bootstrap": "^5.3.3", "clipboard": "^2.0.11", - "qdrant-page-search": "^1.1.2" + "qdrant-page-search": "^1.1.3" }, "devDependencies": { "@babel/preset-env": "^7.23.9", @@ -3112,9 +3112,10 @@ } }, "node_modules/qdrant-page-search": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/qdrant-page-search/-/qdrant-page-search-1.1.2.tgz", - "integrity": "sha512-Wxls/AdDrTDbLu3PFAUceKe/2m4ovzRjDxN362XPV5h3Rm2SrchjenlBlzRvtX3azvINeydT8yREr+Ql3PT1Tw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/qdrant-page-search/-/qdrant-page-search-1.1.3.tgz", + "integrity": "sha512-r+qaZkm241YDrScEwtZFDsokAf+TTAzoCPjDXlNu+Z+ySsl9y8BRH3GvAxWjsISYuszaKQTrpBlQredNjKZdRw==", + "license": "Apache-2.0" }, "node_modules/queue-microtask": { "version": "1.2.3", diff --git a/qdrant-landing/package.json b/qdrant-landing/package.json index bdb80b2fb..6a30691e5 100644 --- a/qdrant-landing/package.json +++ b/qdrant-landing/package.json @@ -21,7 +21,7 @@ "anchor-js": "^5.0.0", "bootstrap": "^5.3.3", "clipboard": "^2.0.11", - "qdrant-page-search": "^1.1.2" + "qdrant-page-search": "^1.1.3" }, "devDependencies": { "@babel/preset-env": "^7.23.9", diff --git a/qdrant-landing/static/admin/config.yml b/qdrant-landing/static/admin/config.yml deleted file mode 100644 index 1d93884d8..000000000 --- a/qdrant-landing/static/admin/config.yml +++ /dev/null @@ -1,51 +0,0 @@ -backend: - name: git-gateway - branch: master # Branch to update (optional; defaults to master) -media_folder: qdrant-landing/static/blog/from_cms -public_folder: /blog/from_cms -collections: - - name: 'blog' - label: 'Blog' - folder: 'qdrant-landing/content/blog' - create: true - slug: '{{slug}}' - editor: - preview: true - fields: - - { label: 'Draft', name: 'draft', widget: 'boolean', default: true, - hint: 'If enabled, the post will be saved but not displayed on the site'} - - { label: 'Title of blog post', name: 'title', widget: 'string' } - - { label: 'Slug', name: 'slug', widget: 'string', required: true, - pattern: [ '^[a-z0-9]+(?:-[a-z0-9]+)*$','A slug can have no spaces or special characters' ], - hint: 'The post URL (do not include folder or file extension)' } - - { label: 'Short Description', name: 'short_description', widget: 'string'} - - { label: 'Description', name: 'description', widget: 'string', - hint: 'Shown in previews of featured posts and on social media' } - - { label: 'Preview Image', name: 'preview_image', widget: 'image', - hint: 'Image should be 1200x630. Ensure that main image content is placed in the middle to make pictures friendly to cropping' } - - { label: 'Publish Date', name: 'date', widget: 'datetime' } - - { label: 'Author', name: 'author', widget: 'string', hint: 'Name'} - - { label: 'Featured Post', name: 'featured', widget: 'boolean', default: false, - hint: 'If enabled, this post will be featured in the "Features and News" blog section. Only the last 4 featured posts will be displayed in this section. Featured posts will not appear in the regular post list.' } - - { label: 'Tags', name: 'tags', widget: 'list', allow_add: true, - hint: 'Use a comma without a space to separate tags (this widget will add spaces after commas itself). Currently, tags only used to show related posts and not appear in user interface. But it can change in the future.' } - - { label: 'Body', name: 'body', widget: 'markdown', modes: ['raw'] } - - name: 'top-banner' - label: 'Top Banner' - create: false - editor: - preview: false - files: - - name: 'top-banner' - label: 'Top Banner' - file: 'qdrant-landing/content/headless/top-banner.md' - fields: - - { label: 'Text', name: 'text', widget: 'string' } - - label: 'Link' - name: 'link' - widget: 'object' - fields: - - { label: 'Text', name: 'text', widget: 'string' } - - { label: 'URL', name: 'url', widget: 'string' } - - { label: 'Start Date', name: 'start', widget: 'datetime', picker_utc: true, hint: 'The banner will be displayed starting from this date, if not set, the banner will not be displayed' } - - { label: 'End Date', name: 'end', widget: 'datetime', picker_utc: true, hint: 'If not set, the banner will be displayed indefinitely' } \ No newline at end of file diff --git a/qdrant-landing/static/admin/emails/confirmation.html b/qdrant-landing/static/admin/emails/confirmation.html deleted file mode 100644 index 348445d82..000000000 --- a/qdrant-landing/static/admin/emails/confirmation.html +++ /dev/null @@ -1,4 +0,0 @@ -

Confirm your signup

- -

Follow this link to confirm your user:

-

Confirm your mail

diff --git a/qdrant-landing/static/admin/emails/email_change.html b/qdrant-landing/static/admin/emails/email_change.html deleted file mode 100644 index f7c6f68a4..000000000 --- a/qdrant-landing/static/admin/emails/email_change.html +++ /dev/null @@ -1,7 +0,0 @@ -

Confirm Change of Email

- -

- Follow this link to confirm the update of your email from - {{ .Email }} to {{ .NewEmail }}: -

-

Change Email

\ No newline at end of file diff --git a/qdrant-landing/static/admin/emails/invite.html b/qdrant-landing/static/admin/emails/invite.html deleted file mode 100644 index 479983155..000000000 --- a/qdrant-landing/static/admin/emails/invite.html +++ /dev/null @@ -1,7 +0,0 @@ -

You have been invited

- -

- You have been invited to create a user on {{ .SiteURL }}. Follow - this link to accept the invite: -

-

Accept the invite

diff --git a/qdrant-landing/static/admin/emails/recovery.html b/qdrant-landing/static/admin/emails/recovery.html deleted file mode 100644 index d3b0de47c..000000000 --- a/qdrant-landing/static/admin/emails/recovery.html +++ /dev/null @@ -1,5 +0,0 @@ -

Reset Password

- -

Follow this link to reset the password for your user:

- -

Reset Password

diff --git a/qdrant-landing/static/admin/index.html b/qdrant-landing/static/admin/index.html deleted file mode 100644 index abf197a0a..000000000 --- a/qdrant-landing/static/admin/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Content Manager - - - - - - - - \ No newline at end of file diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/fde-document-processing.png b/qdrant-landing/static/articles_data/muvera-embeddings/fde-document-processing.png new file mode 100644 index 000000000..a0950207e Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/fde-document-processing.png differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/fde-query-processing.png b/qdrant-landing/static/articles_data/muvera-embeddings/fde-query-processing.png new file mode 100644 index 000000000..ea0efc330 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/fde-query-processing.png differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/muvera-high-level.png b/qdrant-landing/static/articles_data/muvera-embeddings/muvera-high-level.png new file mode 100644 index 000000000..97b93be75 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/muvera-high-level.png differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.jpg b/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.jpg new file mode 100644 index 000000000..3d0513411 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.jpg differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.webp b/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.webp new file mode 100644 index 000000000..adbf8eb0a Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/preview/preview.webp differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/preview/social_preview.jpg b/qdrant-landing/static/articles_data/muvera-embeddings/preview/social_preview.jpg new file mode 100644 index 000000000..99af65f4c Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/preview/social_preview.jpg differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.jpg b/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.jpg new file mode 100644 index 000000000..3f11f9776 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.jpg differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.webp b/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.webp new file mode 100644 index 000000000..971a40200 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/preview/title.webp differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/random-projection.png b/qdrant-landing/static/articles_data/muvera-embeddings/random-projection.png new file mode 100644 index 000000000..b6a57e9b0 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/random-projection.png differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/simhash-cluster-assignment.png b/qdrant-landing/static/articles_data/muvera-embeddings/simhash-cluster-assignment.png new file mode 100644 index 000000000..6238e344b Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/simhash-cluster-assignment.png differ diff --git a/qdrant-landing/static/articles_data/muvera-embeddings/simhash-space-partitioning.png b/qdrant-landing/static/articles_data/muvera-embeddings/simhash-space-partitioning.png new file mode 100644 index 000000000..f741b20b2 Binary files /dev/null and b/qdrant-landing/static/articles_data/muvera-embeddings/simhash-space-partitioning.png differ diff --git a/qdrant-landing/static/blog/case-study-fieldy/case-study-fieldy-bento-dark.jpg b/qdrant-landing/static/blog/case-study-fieldy/case-study-fieldy-bento-dark.jpg new file mode 100644 index 000000000..435e7aa2e Binary files /dev/null and b/qdrant-landing/static/blog/case-study-fieldy/case-study-fieldy-bento-dark.jpg differ diff --git a/qdrant-landing/static/blog/case-study-fieldy/fieldy-device-image.jpg b/qdrant-landing/static/blog/case-study-fieldy/fieldy-device-image.jpg new file mode 100644 index 000000000..8a12d0ec7 Binary files /dev/null and b/qdrant-landing/static/blog/case-study-fieldy/fieldy-device-image.jpg differ diff --git a/qdrant-landing/static/blog/case-study-fieldy/social_preview_partnership-fieldy.jpg b/qdrant-landing/static/blog/case-study-fieldy/social_preview_partnership-fieldy.jpg new file mode 100644 index 000000000..cf70c040a Binary files /dev/null and b/qdrant-landing/static/blog/case-study-fieldy/social_preview_partnership-fieldy.jpg differ diff --git a/qdrant-landing/static/blog/case-study-gooddata/gooddata-diagram-1.1.png b/qdrant-landing/static/blog/case-study-gooddata/gooddata-diagram-1.1.png new file mode 100644 index 000000000..5e7bf1ec5 Binary files /dev/null and b/qdrant-landing/static/blog/case-study-gooddata/gooddata-diagram-1.1.png differ diff --git a/qdrant-landing/static/blog/case-study-opentable/opentable-concierge-screenshot.png b/qdrant-landing/static/blog/case-study-opentable/opentable-concierge-screenshot.png new file mode 100644 index 000000000..4ce8e70ed Binary files /dev/null and b/qdrant-landing/static/blog/case-study-opentable/opentable-concierge-screenshot.png differ diff --git a/qdrant-landing/static/blog/case-study-opentable/social-preview-opentable.jpg b/qdrant-landing/static/blog/case-study-opentable/social-preview-opentable.jpg new file mode 100644 index 000000000..9bf2ada2d Binary files /dev/null and b/qdrant-landing/static/blog/case-study-opentable/social-preview-opentable.jpg differ diff --git a/qdrant-landing/static/blog/case-study-tavus/social_preview_partnership-tavus.jpg b/qdrant-landing/static/blog/case-study-tavus/social_preview_partnership-tavus.jpg new file mode 100644 index 000000000..fac9703ab Binary files /dev/null and b/qdrant-landing/static/blog/case-study-tavus/social_preview_partnership-tavus.jpg differ diff --git a/qdrant-landing/static/blog/case-study-tavus/tavus-bento-box-dark.jpg b/qdrant-landing/static/blog/case-study-tavus/tavus-bento-box-dark.jpg new file mode 100644 index 000000000..192beb87f Binary files /dev/null and b/qdrant-landing/static/blog/case-study-tavus/tavus-bento-box-dark.jpg differ diff --git a/qdrant-landing/static/blog/decay-functions/decay_1.png b/qdrant-landing/static/blog/decay-functions/decay_1.png new file mode 100644 index 000000000..3f2a1792b Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/decay_1.png differ diff --git a/qdrant-landing/static/blog/decay-functions/decay_2.png b/qdrant-landing/static/blog/decay-functions/decay_2.png new file mode 100644 index 000000000..8c816031e Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/decay_2.png differ diff --git a/qdrant-landing/static/blog/decay-functions/decay_3.png b/qdrant-landing/static/blog/decay-functions/decay_3.png new file mode 100644 index 000000000..30bb6750c Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/decay_3.png differ diff --git a/qdrant-landing/static/blog/decay-functions/preview/preview.jpg b/qdrant-landing/static/blog/decay-functions/preview/preview.jpg new file mode 100644 index 000000000..56015f2c2 Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/preview/preview.jpg differ diff --git a/qdrant-landing/static/blog/decay-functions/preview/preview.webp b/qdrant-landing/static/blog/decay-functions/preview/preview.webp new file mode 100644 index 000000000..879917f8c Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/preview/preview.webp differ diff --git a/qdrant-landing/static/blog/decay-functions/preview/social_preview.png b/qdrant-landing/static/blog/decay-functions/preview/social_preview.png new file mode 100644 index 000000000..3a99ef963 Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/preview/social_preview.png differ diff --git a/qdrant-landing/static/blog/decay-functions/preview/title.jpg b/qdrant-landing/static/blog/decay-functions/preview/title.jpg new file mode 100644 index 000000000..f7ea201ff Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/preview/title.jpg differ diff --git a/qdrant-landing/static/blog/decay-functions/preview/title.webp b/qdrant-landing/static/blog/decay-functions/preview/title.webp new file mode 100644 index 000000000..c9a8efdee Binary files /dev/null and b/qdrant-landing/static/blog/decay-functions/preview/title.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/banner.png b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/banner.png new file mode 100644 index 000000000..5e5822381 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/banner.png differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/black-dress-problem.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/black-dress-problem.webp new file mode 100644 index 000000000..891b19476 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/black-dress-problem.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/fashion-search-demo.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/fashion-search-demo.webp new file mode 100644 index 000000000..e1d56b1b4 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/fashion-search-demo.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-food-diversity.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-food-diversity.webp new file mode 100644 index 000000000..ac233c022 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-food-diversity.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-search-results.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-search-results.webp new file mode 100644 index 000000000..742dcda56 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/mmr-search-results.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.jpg b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.jpg new file mode 100644 index 000000000..9eb5f628a Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.jpg differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.webp new file mode 100644 index 000000000..717e03508 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/preview.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.jpg b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.jpg new file mode 100644 index 000000000..8a951e076 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.jpg differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.png b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.png new file mode 100644 index 000000000..b68d4053a Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/social_preview.png differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.jpg b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.jpg new file mode 100644 index 000000000..86a0da46b Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.jpg differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.webp new file mode 100644 index 000000000..c422f21d5 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/preview/title.webp differ diff --git a/qdrant-landing/static/blog/mmr-diversity-aware-reranking/standard-search-results.webp b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/standard-search-results.webp new file mode 100644 index 000000000..3b65375e1 Binary files /dev/null and b/qdrant-landing/static/blog/mmr-diversity-aware-reranking/standard-search-results.webp differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025-lineup/lineup-hero.jpg b/qdrant-landing/static/blog/vector-space-day-2025-lineup/lineup-hero.jpg new file mode 100644 index 000000000..6fa79f595 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025-lineup/lineup-hero.jpg differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/VSD-25.08.png b/qdrant-landing/static/blog/vector-space-day-2025/VSD-25.08.png new file mode 100644 index 000000000..31d2a29d3 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/VSD-25.08.png differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/Vector-Space-Day-Partners_28-Aug.jpg b/qdrant-landing/static/blog/vector-space-day-2025/Vector-Space-Day-Partners_28-Aug.jpg new file mode 100644 index 000000000..454ece313 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/Vector-Space-Day-Partners_28-Aug.jpg differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/hackathon-26aug.png b/qdrant-landing/static/blog/vector-space-day-2025/hackathon-26aug.png new file mode 100644 index 000000000..a7b383397 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/hackathon-26aug.png differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/hackathon.jpg b/qdrant-landing/static/blog/vector-space-day-2025/hackathon.jpg new file mode 100644 index 000000000..9e1d88444 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/hackathon.jpg differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/media-partner.png b/qdrant-landing/static/blog/vector-space-day-2025/media-partner.png new file mode 100644 index 000000000..9254085a0 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/media-partner.png differ diff --git a/qdrant-landing/static/blog/vector-space-day-2025/partners-20.08.png b/qdrant-landing/static/blog/vector-space-day-2025/partners-20.08.png new file mode 100644 index 000000000..eb3f1e4f8 Binary files /dev/null and b/qdrant-landing/static/blog/vector-space-day-2025/partners-20.08.png differ diff --git a/qdrant-landing/static/courses/course-integrations/aparavi.svg b/qdrant-landing/static/courses/course-integrations/aparavi.svg new file mode 100644 index 000000000..0603484b8 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/aparavi.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/camel-ai.svg b/qdrant-landing/static/courses/course-integrations/camel-ai.svg new file mode 100644 index 000000000..ef2106e2a --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/camel-ai.svg @@ -0,0 +1,3 @@ + + + diff --git a/qdrant-landing/static/courses/course-integrations/crew-ai.svg b/qdrant-landing/static/courses/course-integrations/crew-ai.svg new file mode 100755 index 000000000..ca7d0be94 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/crew-ai.svg @@ -0,0 +1,3 @@ + + + diff --git a/qdrant-landing/static/courses/course-integrations/haystack.svg b/qdrant-landing/static/courses/course-integrations/haystack.svg new file mode 100644 index 000000000..337baa3d8 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/haystack.svg @@ -0,0 +1,3 @@ + + + diff --git a/qdrant-landing/static/courses/course-integrations/jina.svg b/qdrant-landing/static/courses/course-integrations/jina.svg new file mode 100644 index 000000000..c2f40184a --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/jina.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/n8n.svg b/qdrant-landing/static/courses/course-integrations/n8n.svg new file mode 100644 index 000000000..932e65c40 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/n8n.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/quotient.svg b/qdrant-landing/static/courses/course-integrations/quotient.svg new file mode 100644 index 000000000..714d200a8 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/quotient.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/superlinked.svg b/qdrant-landing/static/courses/course-integrations/superlinked.svg new file mode 100644 index 000000000..e7bde4d96 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/superlinked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/qdrant-landing/static/courses/course-integrations/tensorlake.svg b/qdrant-landing/static/courses/course-integrations/tensorlake.svg new file mode 100644 index 000000000..21a13a494 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/tensorlake.svg @@ -0,0 +1,4 @@ + + + + diff --git a/qdrant-landing/static/courses/course-integrations/twelveLabs.svg b/qdrant-landing/static/courses/course-integrations/twelveLabs.svg new file mode 100644 index 000000000..a5294c163 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/twelveLabs.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/unstructured.svg b/qdrant-landing/static/courses/course-integrations/unstructured.svg new file mode 100644 index 000000000..cd7a82032 --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/unstructured.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/qdrant-landing/static/courses/course-integrations/vectorize.svg b/qdrant-landing/static/courses/course-integrations/vectorize.svg new file mode 100644 index 000000000..d6bd26d9d --- /dev/null +++ b/qdrant-landing/static/courses/course-integrations/vectorize.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/qdrant-landing/static/llms-full.txt b/qdrant-landing/static/llms-full.txt index d059df3c6..e588b9931 100644 --- a/qdrant-landing/static/llms-full.txt +++ b/qdrant-landing/static/llms-full.txt @@ -12512,9 +12512,11 @@ storage: # Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index. ef_construct: 100 - # Minimal size (in KiloBytes) of vectors for additional payload-based indexing. - # If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used - - # in this case full-scan search should be preferred by query planner and additional indexing is not required. + # Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search. + # This measures the total size of vectors being queried against. + # When the maximum estimated amount of points that a condition satisfies is smaller than + # `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index + # traversal for better performance. # Note: 1Kb = 1 vector of size 256 full_scan_threshold_kb: 10000 @@ -28810,9 +28812,11 @@ storage: # Number of neighbours to consider during the index building. # Larger the value - more accurate the search, more time required to build index. ef_construct: 100 - # Minimal size (in KiloBytes) of vectors for additional payload-based indexing. - # If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used - - # in this case full-scan search should be preferred by query planner and additional indexing is not required. + # Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search. + # This measures the total size of vectors being queried against. + # When the maximum estimated amount of points that a condition satisfies is smaller than + # `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index + # traversal for better performance. # Note: 1Kb = 1 vector of size 256 full_scan_threshold: 10000 @@ -34510,13 +34514,14 @@ POST /collections/{collection_name}/points/query "query": { "formula": { "sum": [\ - "$score,\ + "$score",\ {\ "mult": [\ 0.5,\ {\ "key": "tag",\ - "match": { "any": ["h1", "h2", "h3", "h4"] } }\ + "match": { "any": ["h1", "h2", "h3", "h4"] }\ + }\ ]\ },\ {\ diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/_components.scss b/qdrant-landing/themes/qdrant-2024/assets/css/_components.scss index 9dfd913fd..6e1a8ed23 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/_components.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/_components.scss @@ -6,3 +6,8 @@ @import 'components/copy-btn'; @import 'components/lang-switcher'; @import 'components/alerts'; +@import 'components/list'; +@import 'components/cards-list'; +@import 'components/accordion'; +@import 'components/course-card'; +@import 'components/date'; diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/components/_accordion.scss b/qdrant-landing/themes/qdrant-2024/assets/css/components/_accordion.scss new file mode 100644 index 000000000..46e07b876 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/assets/css/components/_accordion.scss @@ -0,0 +1,70 @@ +@use '../helpers/functions' as *; +@use 'sass:math'; + +.accordion-dark { + display: flex; + flex-direction: column; + gap: $spacer * 1.5; + width: 100%; + margin-bottom: $spacer * 2; + + &__item { + border: pxToRem(1) solid $neutral-20; + background: linear-gradient($neutral-20, #0e1424); + border-radius: pxToRem(12); + + &-header { + position: relative; + display: flex; + align-items: center; + margin-bottom: 0; + padding: $spacer * 2 $spacer * 4 $spacer * 2 $spacer * 2; + color: $neutral-94; + font-size: $font-size-l; + line-height: $line-height-lg; + font-weight: 500; + cursor: pointer; + + &:after { + content: ''; + display: block; + position: absolute; + right: $spacer * 2; + top: calc(50% - pxToRem(9)); + height: pxToRem(18); + width: pxToRem(16); + background-image: url("data:image/svg+xml,%3Csvg width='17' height='19' viewBox='0 0 17 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_10609_1497)'%3E%3Cpath d='M1.81787 6.50513L8.63605 13.3233L15.4542 6.50513' stroke='%23E1E5F0' stroke-width='1.36364' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_10609_1497'%3E%3Crect width='16.3636' height='16.3636' fill='white' transform='translate(0.63623 1.91431)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"); + background-position: center; + background-repeat: no-repeat; + background-size: cover; + } + } + + &-body { + max-height: 0; + overflow: hidden; + transition: max-height 0.2s ease-out; + + &-content { + padding: 0 $spacer * 2 $spacer * 2 $spacer * 2; + color: $neutral-70; + + [data-theme='light'] & { + color: $neutral-70; + } + + ul { + li:not(:last-of-type) { + margin-bottom: pxToRem(10); + } + } + } + } + + &.active { + .accordion-dark__item-header:after { + background-image: url("data:image/svg+xml,%3Csvg width='17' height='19' viewBox='0 0 17 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_10854_1606)'%3E%3Cpath d='M15.4546 13.3232L8.63641 6.50506L1.81823 13.3232' stroke='%23E1E5F0' stroke-width='1.36364' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_10854_1606'%3E%3Crect width='16.3636' height='16.3636' fill='white' transform='translate(0.63623 1.91418)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"); + } + } + } +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/components/_cards-list.scss b/qdrant-landing/themes/qdrant-2024/assets/css/components/_cards-list.scss new file mode 100644 index 000000000..ece6c914e --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/assets/css/components/_cards-list.scss @@ -0,0 +1,50 @@ +@use '../helpers/functions' as *; +@use 'sass:math'; + +.cards-list { + display: flex; + flex-wrap: wrap; + gap: $spacer * 1.5 $spacer * 2; + + &__item { + display: flex; + align-items: flex-start; + gap: $spacer; + width: 100%; + padding: $spacer; + border: pxToRem(1) solid $neutral-20; + background: linear-gradient($neutral-20, #0e1424); + border-radius: pxToRem(12); + + &-content { + margin-bottom: 0; + font-size: $font-size-s; + line-height: $line-height-sm; + color: $neutral-70; + } + + &-title { + margin-bottom: math.div($spacer, 4); + font-size: $font-size-l; + line-height: $line-height-lg; + color: $neutral-98; + } + + img.cards-list__item-image { + flex-shrink: 0; + width: $spacer * 3; + height: auto; + margin: 0; + } + } + + @include media-breakpoint-up(lg) { + &__item { + width: calc(50% - $spacer); + padding: $spacer; + border: pxToRem(1) solid $neutral-20; + background: linear-gradient($neutral-20, #0e1424); + border-radius: pxToRem(12); + } + } +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/components/_course-card.scss b/qdrant-landing/themes/qdrant-2024/assets/css/components/_course-card.scss new file mode 100644 index 000000000..c8c6826a8 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/assets/css/components/_course-card.scss @@ -0,0 +1,43 @@ +@use '../helpers/functions' as *; +@use "sass:math"; + +.course-card { + width: 100%; + margin: $spacer * 2.5 0 $spacer; + padding: $spacer * 2; + border: pxToRem(1) solid $neutral-20; + background: linear-gradient($neutral-20, #0e1424); + border-radius: pxToRem(12); + + &__title { + display: flex; + justify-content: flex-start; + align-items: center; + gap: $spacer; + margin-bottom: $spacer * 1.5; + font-size: $font-size-xl; + line-height: pxToRem(30); + font-weight: 500; + color: $neutral-98; + } + + .course-card__title img { + display: block; + margin: 0; + height: pxToRem(20); + width: pxToRem(20); + } + + &__content { + [data-theme='light'] & { + color: $neutral-70; + } + } + + &__button { + margin-top: $spacer * 1.5; + font-size: $font-size-s; + line-height: $line-height-sm; + font-weight: 500; + } +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/components/_date.scss b/qdrant-landing/themes/qdrant-2024/assets/css/components/_date.scss new file mode 100644 index 000000000..48e7199f3 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/assets/css/components/_date.scss @@ -0,0 +1,17 @@ +@use '../helpers/functions' as *; +@use "sass:math"; + +.date { + display: flex; + justify-content: flex-start; + align-items: center; + gap: math.div($spacer, 2); + margin-bottom: math.div($spacer, 2); + color: $secondary-blue-50; + + &-icon { + width: $spacer !important; + height: $spacer !important; + margin: 0 !important; + } +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/components/_list.scss b/qdrant-landing/themes/qdrant-2024/assets/css/components/_list.scss new file mode 100644 index 000000000..d0b942aff --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/assets/css/components/_list.scss @@ -0,0 +1,66 @@ +@use '../helpers/functions' as *; +@use "sass:math"; + +.list { + color: $neutral-70; + + ul { + list-style: none; + margin-bottom: 0; + padding-left: 0; + + li { + position: relative; + padding-left: pxToRem(22); + + &:not(:last-of-type) { + margin-bottom: pxToRem(12); + } + + &:before { + content: ""; + position: absolute; + left: 0; + top: pxToRem(5); + width: pxToRem(14); + height: pxToRem(14); + background-image: url("data:image/svg+xml,%3Csvg width='14' height='15' viewBox='0 0 14 15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_10609_1587)'%3E%3Cpath d='M12.6875 4.56921V10.9862L7 14.1942L1.3125 10.9862V4.56921L7 1.36121L12.6875 4.56921ZM9.53223 5.87976L6.05371 8.92371L4.08301 6.953L3.25781 7.77722L6.00098 10.5204L6.41113 10.161L10.3008 6.75867L10.7393 6.3739L9.97168 5.49597L9.53223 5.87976Z' fill='%238F98B3'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_10609_1587'%3E%3Crect width='14' height='14' fill='white' transform='translate(0 0.777832)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"); + background-size: contain; + background-repeat: no-repeat; + } + } + } + + &.list-completed { + ul { + li { + padding-left: pxToRem(26); + + &:before { + top: pxToRem(3); + width: pxToRem(18); + height: pxToRem(18); + background-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.3125 4.875V13.125L9 17.25L1.6875 13.125V4.875L9 0.75L16.3125 4.875ZM12.2559 6.56055L7.7832 10.4727L5.25 7.93945L4.18945 9L7.71582 12.5264L8.24414 12.0645L13.2441 7.68945L13.8086 7.19531L12.8203 6.06641L12.2559 6.56055Z' fill='%23008A53'/%3E%3C/svg%3E%0A"); + } + } + } + } + + &.list-wide { + ul { + -webkit-column-count: 2; + -moz-column-count: 2; + column-count: 2; + column-gap: $spacer * 6; + margin-bottom: -$spacer * 1.5; + + li { + margin-bottom: $spacer * 1.5; + } + } + } + + [data-theme='light'] & { + color: $neutral-30; + } +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_community-features.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_community-features.scss index a623cfa25..8830fcb04 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_community-features.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_community-features.scss @@ -14,47 +14,6 @@ text-align: center; } - &__resources-card { - padding-bottom: 0; - overflow: hidden; - - &-title { - font-size: $spacer * 1.5; - line-height: pxToRem(34); - color: $neutral-20; - margin-bottom: pxToRem(12); - } - - &-description { - color: $neutral-40; - margin-bottom: pxToRem(12); - } - - &-link { - @include mixins.link-color($neutral-20); - } - - &-image { - width: 100%; - align-self: center; - margin-top: $spacer * 1.5; - } - - &:hover { - .community-features__resources-card-title { - color: $primary-50; - } - } - } - - &__resources-cards { - margin-bottom: pxToRem(30); - - img { - min-width: pxToRem(635); - } - } - &__card { &-icon { height: $spacer * 1.5; @@ -96,18 +55,6 @@ margin-bottom: $spacer * 5; } - &__resources-card { - justify-content: flex-start; - - &-link { - margin-bottom: $spacer; - } - - &-image { - margin-top: auto; - } - } - &__card { justify-content: flex-start; diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_customer-list.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_customer-list.scss index ec6a4b526..7bbc21468 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_customer-list.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_customer-list.scss @@ -7,7 +7,7 @@ background-color: $neutral-94; &__logos { - @include marquee.base(64px, 224px, 18, 18, 52px, $neutral-94, false, 50s, block); + @include marquee.base(64px, 224px, 17, 17, 52px, $neutral-94, false, 50s, block); } &__title { @@ -25,7 +25,7 @@ background-color: $neutral-20; .customer-list__logos { - @include marquee.base(64px, 224px, 18, 18, 52px, $neutral-20, false, 50s, block); + @include marquee.base(64px, 224px, 17, 17, 52px, $neutral-20, false, 50s, block); } .customer-list__title { diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_table-of-contents.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_table-of-contents.scss index 1a2c1ba83..3177fede8 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/_table-of-contents.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/_table-of-contents.scss @@ -107,6 +107,13 @@ } } + &__button { + width: 100%; + font-size: $font-size-s; + line-height: $line-height-sm; + color: $neutral-100; + } + @include media-breakpoint-up(xl) { max-height: calc(100vh - 80px); width: pxToRem(232); @@ -134,7 +141,7 @@ color: $neutral-98; } - a { + a:not(.table-of-contents__button) { color: $neutral-70; transition: all 0.3s; &:hover { diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs-footer.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs-footer.scss index 126f9d644..d117a9e91 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs-footer.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs-footer.scss @@ -8,8 +8,7 @@ justify-content: center; align-items: center; padding: $spacer * 2.5 0; - border-top: 1px solid $neutral-20; - border-bottom: 1px solid $neutral-20; + border-top: pxToRem(1) solid $neutral-20; color: $neutral-98; h4 { @@ -22,6 +21,7 @@ } &__bottom { + border-top: pxToRem(1) solid $neutral-20; @extend .footer__bottom; } diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs.scss index c46a44350..63bf12b11 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_docs.scss @@ -100,6 +100,13 @@ text-align: center; z-index: 2; + h1 { + font-size: $spacer * 2; + line-height: $spacer * 2.5; + padding: 0; + margin-bottom: math.div($spacer, 2); + } + h3 { font-size: pxToRem(20); line-height: pxToRem(30); @@ -108,6 +115,12 @@ margin-bottom: $spacer * 1.5; } + p { + font-size: pxToRem(18); + line-height: pxToRem(27); + margin-bottom: 0; + } + a { font-size: pxToRem(14); font-weight: 500; @@ -207,6 +220,8 @@ text-align: left; h3, + h1, + p, a { margin-left: $spacer * 2.5; } @@ -256,9 +271,12 @@ } } .docs-core__developing-block { - h3 { + h1, h3 { color: $neutral-98; } + p { + color: $neutral-70; + } .button_outlined { color: $neutral-100; box-shadow: 0 0 0 1px $neutral-60; diff --git a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_documentation-menu.scss b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_documentation-menu.scss index c2713400b..ff2d5b2ef 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_documentation-menu.scss +++ b/qdrant-landing/themes/qdrant-2024/assets/css/partials/documentation/_documentation-menu.scss @@ -101,7 +101,7 @@ } &.active { - .docs-menu__links-group-heading>a { + &>.docs-menu__links-group-heading>a { font-weight: 600; background-color: $neutral-30; color: $neutral-90; @@ -177,6 +177,34 @@ } } + &__progress { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 math.div($spacer, 2); + + &-container { + width: pxToRem(168); + height: pxToRem(10); + background-color: $neutral-20; + border-radius: $spacer; + overflow: hidden; + } + + &-bar { + height: 100%; + border-radius: $spacer; + background-color: $primary-50; + } + + &-text { + margin-bottom: 0; + font-size: $font-size-xs; + line-height: $font-size-l; + color: $neutral-70; + } + } + @include media-breakpoint-up(xl) { display: block; height: calc(100vh - 80px); @@ -220,7 +248,7 @@ &__links-group { &.active { - .docs-menu__links-group-heading>a { + &>.docs-menu__links-group-heading>a { background-color: $neutral-20; } @@ -345,7 +373,7 @@ &__links-group { &.active { - .docs-menu__links-group-heading>a { + &>.docs-menu__links-group-heading>a { color: $neutral-30; } } @@ -376,4 +404,4 @@ } } } -} \ No newline at end of file +} diff --git a/qdrant-landing/themes/qdrant-2024/assets/js/helpers.js b/qdrant-landing/themes/qdrant-2024/assets/js/helpers.js index 10ebbd5a1..f2a15433c 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/js/helpers.js +++ b/qdrant-landing/themes/qdrant-2024/assets/js/helpers.js @@ -1,3 +1,5 @@ +const UTM_PARAMS_KEY = 'utm_params'; + export function isElementInViewport(el) { var rect = el.getBoundingClientRect(); @@ -105,54 +107,56 @@ export function addGA4Properties(properties) { properties.ga_client_id = getCookie('_ga')?.replace('GA1.1.',''); } +export function persistUTMParams() { + if (!window.location.search) return; + + const utmParams = getUTMParams(); + + if (!Object.keys(utmParams).length) return; + + let filteredParams = ''; + let ampersand = false; + + for (const key in utmParams) { + if (utmParams[key]) { + ampersand = filteredParams.length; + filteredParams += `${ampersand ? '&' : ''}${key}=${utmParams[key]}`; + } + } + + const oneYearInSeconds = 365 * 24 * 60 * 60; + document.cookie = `${UTM_PARAMS_KEY}=${filteredParams}; path=/; max-age=${oneYearInSeconds}`; +} + export function getUTMParams() { - const urlParams = new URLSearchParams(window.location.search); + const search = window.location.search; + + if (!search) return {}; - // Gather all GTM related params - const utmIds = { - gcl: urlParams.get('gclid'), - gbra: urlParams.get('gbraid'), - wbra: urlParams.get('wbraid'), + const urlParams = new URLSearchParams(search); + + return { + gclid: urlParams.get('gclid'), + gbraid: urlParams.get('gbraid'), + wbraid: urlParams.get('wbraid'), + utm_source: urlParams.get('utm_source'), + utm_medium: urlParams.get('utm_medium'), + utm_campaign: urlParams.get('utm_campaign'), + utm_content: urlParams.get('utm_content'), + utm_term: urlParams.get('utm_term') }; - - const utmParams = { - source: urlParams.get('utm_source'), - medium: urlParams.get('utm_medium'), - campaign: urlParams.get('utm_campaign'), - content: urlParams.get('utm_content'), - term: urlParams.get('utm_term') - }; - - return [utmIds, utmParams]; } export function addUTMToLinks() { - const [utmIds, utmParams] = getUTMParams(); - - // Create new params string for outbound links and store in sessionStorage - let newParams = ''; - for (const key in utmIds) { - if (utmIds[key]) { - sessionStorage.setItem(`${key}id`, utmIds[key]); - newParams += `${key}id=${utmIds[key]}&`; - } - } - for (const key in utmParams) { - if (utmParams[key]) { - sessionStorage.setItem(`utm_${key}`, utmParams[key]); - newParams += `utm_${key}=${utmParams[key]}&`; - } - } + const utmParams = getCookie(UTM_PARAMS_KEY); // Add url params to outbound links to product site - if (newParams.length > 0) { - newParams = newParams.replace(/[&|?]$/, ''); // remove trailing & or ? - + if (utmParams) { const links = document.querySelectorAll('a[href*="cloud.qdrant.io"]'); links.forEach(link => { const href = link.href; const separator = href.indexOf('?') === -1 ? '?' : '&'; - link.href = `${href}${separator}${newParams}`; + link.href = `${href}${separator}${utmParams}&qdrant_ref=qdrant_tech`; }); } } diff --git a/qdrant-landing/themes/qdrant-2024/assets/js/index.js b/qdrant-landing/themes/qdrant-2024/assets/js/index.js index eff6f9301..b2da1238b 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/js/index.js +++ b/qdrant-landing/themes/qdrant-2024/assets/js/index.js @@ -1,10 +1,12 @@ import scrollHandler from './scroll-handler'; import { XXL_BREAKPOINT } from './constants'; -import { initGoToTopButton } from './helpers'; +import { initGoToTopButton, persistUTMParams } from './helpers'; import { handleSegmentReady } from './segment-helpers'; import { registerAndCall } from './onetrust-helpers'; import TableOfContents from './table-of-content'; +persistUTMParams(); + // on document ready document.addEventListener('DOMContentLoaded', function () { const handleOneTrustLoaded = () => { // One Trust Loaded @@ -128,4 +130,25 @@ document.addEventListener('DOMContentLoaded', function () { window.location.href = url; }); }); + + function toggleAccordion() { + this.parentElement.classList.toggle('active'); + const panel = this.nextElementSibling; + if (panel.style.maxHeight) { + panel.style.maxHeight = null; + } else { + panel.style.maxHeight = panel.scrollHeight + 'px'; + } + } + + const accordionButtons = Array.from(document.getElementsByClassName('accordion__item-header')); + accordionButtons.forEach((el) => { + el.addEventListener('click', toggleAccordion); + }); + + const accordionDarkButtons = Array.from(document.getElementsByClassName('accordion-dark__item-header')); + accordionDarkButtons.forEach((el) => { + el.addEventListener('click', toggleAccordion); + }); + }); diff --git a/qdrant-landing/themes/qdrant-2024/assets/js/search/search.js b/qdrant-landing/themes/qdrant-2024/assets/js/search/search.js index 29c88bcb3..a177adec1 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/js/search/search.js +++ b/qdrant-landing/themes/qdrant-2024/assets/js/search/search.js @@ -19,4 +19,8 @@ import 'qdrant-page-search/dist/js/search.min.js'; if (/blog/.test(window.location?.pathname)) { window.initQdrantSearch({ searchApiUrl: 'https://search.qdrant.tech/api/search', section: 'blog', partition: partition }); } + + if (/course/.test(window.location?.pathname)) { + window.initQdrantSearch({ searchApiUrl: 'https://search.qdrant.tech/api/search', section: 'course', partition: partition }); + } })(); diff --git a/qdrant-landing/themes/qdrant-2024/assets/js/segment-helpers.js b/qdrant-landing/themes/qdrant-2024/assets/js/segment-helpers.js index c5e0bb402..23c09f858 100644 --- a/qdrant-landing/themes/qdrant-2024/assets/js/segment-helpers.js +++ b/qdrant-landing/themes/qdrant-2024/assets/js/segment-helpers.js @@ -140,6 +140,23 @@ const trackInteractionEvent = (properties = {}) => { ) } +function cleanSegmentUtmKeys(obj) { + const cleanedObject = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + // Remove "id" from the end + let cleanedKey = key.replace(/id$/, ''); + + // Remove "utm_" from the beginning + cleanedKey = cleanedKey.replace(/^utm_/, ''); + + cleanedObject[cleanedKey] = obj[key]; + } + } + return cleanedObject; +} + + /************************/ /* Handle Segment Ready */ /************************/ @@ -147,15 +164,16 @@ export function handleSegmentReady() { addUTMToLinks(); analytics.ready(() => { - const [utmIds, utmParams] = getUTMParams(); + const utmParams = getUTMParams() + const cleanUtmParams = cleanSegmentUtmKeys(utmParams); + const isFirstPageView = localStorage.getItem('isFirstPageView'); if (isFirstPageView === 'true') { analytics.identify({ firstVisitAttribution: { referrer: document.referrer, - ...utmParams, - ...utmIds + ...cleanUtmParams }, hubspotutk: getCookie('hubspotutk'), }); diff --git a/qdrant-landing/themes/qdrant-2024/layouts/_default/sitemap.xml b/qdrant-landing/themes/qdrant-2024/layouts/_default/sitemap.xml index 82fb335d3..e87d83128 100644 --- a/qdrant-landing/themes/qdrant-2024/layouts/_default/sitemap.xml +++ b/qdrant-landing/themes/qdrant-2024/layouts/_default/sitemap.xml @@ -2,6 +2,15 @@ {{ range .Data.Pages }} + + + + {{ if eq .Section "course" }} + {{ continue }} + {{ end }} + + + {{- if and (or (eq (isset .Params "sitemapexclude") false) (ne .Params.sitemapExclude true)) (ne .Permalink "") }} {{ .Permalink }}{{ if not .Lastmod.IsZero }} diff --git a/qdrant-landing/themes/qdrant-2024/layouts/course/list.html b/qdrant-landing/themes/qdrant-2024/layouts/course/list.html new file mode 100644 index 000000000..a75dbe254 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/layouts/course/list.html @@ -0,0 +1,11 @@ + + + {{- partial "head.html" . -}} + +
+ {{ partial "documentation/header" . }} + {{ partial "course/content-layout" . }} +
+ + {{ partial "js.html" . }} + diff --git a/qdrant-landing/themes/qdrant-2024/layouts/course/single.html b/qdrant-landing/themes/qdrant-2024/layouts/course/single.html new file mode 100644 index 000000000..a75dbe254 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/layouts/course/single.html @@ -0,0 +1,11 @@ + + + {{- partial "head.html" . -}} + +
+ {{ partial "documentation/header" . }} + {{ partial "course/content-layout" . }} +
+ + {{ partial "js.html" . }} + diff --git a/qdrant-landing/themes/qdrant-2024/layouts/get_anonymous_id/baseof.html b/qdrant-landing/themes/qdrant-2024/layouts/get_anonymous_id/baseof.html index 81a6684d3..db1c8e6e5 100644 --- a/qdrant-landing/themes/qdrant-2024/layouts/get_anonymous_id/baseof.html +++ b/qdrant-landing/themes/qdrant-2024/layouts/get_anonymous_id/baseof.html @@ -1,6 +1,7 @@ + Get Anonymous ID + + diff --git a/qdrant-landing/themes/qdrant-2024/layouts/partials/community-features.html b/qdrant-landing/themes/qdrant-2024/layouts/partials/community-features.html index 82fb97353..11b2ee1ea 100644 --- a/qdrant-landing/themes/qdrant-2024/layouts/partials/community-features.html +++ b/qdrant-landing/themes/qdrant-2024/layouts/partials/community-features.html @@ -2,22 +2,6 @@

{{ .Params.title }}

- -
- {{ range $index, $element := .Params.resources }} - - {{ end }} -
-
{{ range $index, $element := .Params.features }}
diff --git a/qdrant-landing/themes/qdrant-2024/layouts/partials/course/content-layout.html b/qdrant-landing/themes/qdrant-2024/layouts/partials/course/content-layout.html new file mode 100644 index 000000000..192bfef07 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/layouts/partials/course/content-layout.html @@ -0,0 +1,74 @@ + +
+
+
+ {{ partial "course/sidebar-menu.html" (dict "context" .) }} + +
+
+
+ + {{ if not (eq .RelPermalink "/course/") }} + {{ partial "documentation/breadcrumbs" . }} + {{ end }} + +
+ {{ partial "article-content.html" . }} + + {{ $parent := .Parent }} + {{ if or (eq .Params.isLesson true) (eq $parent.Params.isLesson true) }} +
+ {{ $currentWeight := .Params.weight }} + {{ $targetWeight := add $currentWeight 1 }} + {{ $subject := site.GetPage "course" }} + + {{ $allLessons := where $subject.Pages ".Params.isLesson" true }} + + {{ $allVideos := slice }} + {{ range $lesson := $allLessons }} + {{ $allVideos = $allVideos | append $lesson.RegularPages }} + {{ end }} + + {{ $allPages := $allLessons }} + {{ range $p := $allVideos }} + {{ $allPages = $allPages | append $p }} + {{ end }} + + {{ $nextLesson := where $allPages ".Params.weight" $targetWeight }} + + {{ if gt (len $nextLesson) 0 }} + {{ $currentPage := . }} + {{ $siblings := $currentPage.Parent.RegularPages }} + + {{ $maxWeight := 0 }} + {{ range $p := $siblings }} + {{ if gt (int $p.Params.weight) $maxWeight }} + {{ $maxWeight = (int $p.Params.weight) }} + {{ end }} + {{ end }} + + {{ $next := index $nextLesson 0 }} + + {{ if eq (int $currentPage.Params.weight) $maxWeight }} + {{ $subject.Params.content.nextDay }} {{ .Parent.Title }} + {{ else }} + {{ $subject.Params.content.nextButton }} + {{ end }} + + {{ end }} +
+ {{ end }} +
+
+ + {{ partial "table-of-contents" . }} + + {{ partial "documentation/footer.html" . }} +
+
+
+
+
diff --git a/qdrant-landing/themes/qdrant-2024/layouts/partials/course/sidebar-menu.html b/qdrant-landing/themes/qdrant-2024/layouts/partials/course/sidebar-menu.html new file mode 100644 index 000000000..82c9cb441 --- /dev/null +++ b/qdrant-landing/themes/qdrant-2024/layouts/partials/course/sidebar-menu.html @@ -0,0 +1,142 @@ +{{ $currentNode := .context }} + +
+ +
diff --git a/qdrant-landing/themes/qdrant-2024/layouts/partials/css.html b/qdrant-landing/themes/qdrant-2024/layouts/partials/css.html index 0c9f281fe..a1cc30cb4 100644 --- a/qdrant-landing/themes/qdrant-2024/layouts/partials/css.html +++ b/qdrant-landing/themes/qdrant-2024/layouts/partials/css.html @@ -6,7 +6,7 @@ {{ end }} -{{ if in (slice "blog" "docs" "documentation" "articles") .Section }} +{{ if in (slice "blog" "docs" "documentation" "articles" "course") .Section }} {{ $pageSearchCss := resources.Get "css/search/search.scss" | toCSS $opts | minify | resources.Fingerprint "sha512" }} {{ end }} @@ -21,7 +21,7 @@ /> {{ end }} -{{ if in (slice "docs" "documentation" "articles") .Section }} +{{ if in (slice "docs" "documentation" "articles" "course") .Section }} {{ $documentationCss := resources.Get "css/documentation.scss" | toCSS $opts | minify | resources.Fingerprint "sha512" }} {{ end }} -{{ if not (in (slice "hybrid-cloud" "docs" "documentation" "benchmarks") .Section) }} +{{ if not (in (slice "hybrid-cloud" "docs" "documentation" "course" "benchmarks") .Section) }} {{ $mainCss := resources.Get "css/main.scss" | toCSS $opts | minify | resources.Fingerprint "sha512" }} {{ end }} diff --git a/qdrant-landing/themes/qdrant-2024/layouts/partials/documentation/footer.html b/qdrant-landing/themes/qdrant-2024/layouts/partials/documentation/footer.html index 6cc314056..9da31c0d0 100644 --- a/qdrant-landing/themes/qdrant-2024/layouts/partials/documentation/footer.html +++ b/qdrant-landing/themes/qdrant-2024/layouts/partials/documentation/footer.html @@ -1,11 +1,16 @@ +{{ $currentPage := . }} + {{ with (.Site.GetPage "/headless/footer") }}