Back to Qdrant Internals

Filterable HNSW Without Recall Loss

Andrei Vasnetsov

·

November 24, 2019

Filterable HNSW Without Recall Loss

Note: This article describes the original design for filterable HNSW, proposed in 2019. It is not a complete description of how Qdrant performs filtered vector search today. For learning more, see our Qdrant Beginners course: Fast Approximate Search: HNSW. For the cases this page does not explain, see Filtered Vector Search: What ACORN Fixes, and What Fixes ACORN.

If you need to find some similar objects in vector space, provided e.g. by embeddings or matching NN, you can choose among a variety of libraries: Annoy, FAISS or NMSLib. All of them will give you a fast approximate neighbors search within almost any space.

But what if you need to introduce some constraints in your search? For example, you want search only for products in some category or select the most similar customer of a particular brand. I did not find any simple solutions for this. There are several discussions like this, but they only suggest to iterate over top search results and apply conditions consequently after the search.

Let’s see if we could somehow modify any of ANN algorithms to be able to apply constrains during the search itself.

Annoy builds tree index over random projections. Tree index implies that we will meet same problem that appears in relational databases: if field indexes were built independently, then it is possible to use only one of them at a time. I hadn’t seen this solved elsewhere at the time, which suggested there was no easy fix within the existing tree-index libraries.

There is another algorithm which shows top results on the benchmark. It is called HNSW which stands for Hierarchical Navigable Small World.

The original paper is well written and very easy to read, so I will only give the main idea here. We need to build a navigation graph among all indexed points so that the greedy search on this graph will lead us to the nearest point. This graph is constructed by sequentially adding points that are connected by a fixed number of edges to previously added points. In the resulting graph, the number of edges at each point does not exceed a given threshold $m$ and always contains the nearest considered points.

NSW graph with indexed points, a query, an entry point, and arrows showing the original illustrated traversal.
The navigation graph connects indexed points; arrows illustrate traversal from the entry point toward the query.

How can we modify it?

What if we simply apply the filter criteria to the nodes of this graph and use in the greedy search only those that meet these criteria? It turns out that even with this naive modification algorithm can cover some use cases.

One such case is if your criteria do not correlate with vector semantics. For example, you use a vector search for clothing names and want to filter out some sizes. In this case, the nodes will be uniformly filtered out from the entire cluster structure. Therefore, the theoretical conclusions obtained in the Percolation theory become applicable:

Percolation is related to the robustness of the graph (called also network). Given a random graph of $n$ nodes and an average degree $\langle k\rangle$ . Next we remove randomly a fraction $1-p$ of nodes and leave only a fraction $p$. There exists a critical percolation threshold $ pc = \frac{1}{\langle k\rangle} $ below which the network becomes fragmented while above $pc$ a giant connected component exists.

The 2019 experiments report precision@10, the fraction of returned neighbors among the true 10 nearest points.

Historical precision@10 curves for m values of 8, 16, 24, and 32. Larger m shifts the sharp accuracy drop toward higher removal thresholds.
In the 2019 experiment, more edges shifted the accuracy drop toward higher removal thresholds. The horizontal axis preserves the original mask-threshold parameter.
Historical source data
SeriesExperiment parameterVariable parameterprecision@10Interval lowInterval highAverage position
m = 88300.990.97049860458201211.0
m = 88310.930.87999210370079670.9800078962992034
m = 88321.01.01.0
m = 88330.960.92159270658915750.9984072934108424
m = 88340.950.90728357529205280.9927164247079471
m = 88350.960.92159270658915750.9984072934108424
m = 88360.940.89345343433859490.986546565661405
m = 88370.980.95256050421643921.0
m = 88380.950.90728357529205280.9927164247079471
m = 88390.950.90728357529205280.9927164247079471
m = 88400.960.92159270658915750.9984072934108424
m = 88410.970.93656551904362821.0
m = 88420.920.86682751000723330.9731724899927667
m = 88430.970.93656551904362821.0
m = 88440.960.92159270658915750.9984072934108424
m = 88450.920.86682751000723330.9731724899927667
m = 88460.950.90728357529205280.9927164247079471
m = 88470.980.95256050421643921.0
m = 88480.920.86682751000723330.9731724899927667
m = 88490.930.87999210370079670.9800078962992034
m = 88500.930.87999210370079670.9800078962992034
m = 88510.920.86682751000723330.9731724899927667
m = 88520.950.90728357529205280.9927164247079471
m = 88530.980.95256050421643921.0
m = 88540.990.97049860458201211.0
m = 88550.960.92159270658915750.9984072934108424
m = 88560.970.93656551904362821.0
m = 88570.950.90728357529205280.9927164247079471
m = 88580.920.86682751000723330.9731724899927667
m = 88590.980.95256050421643921.0
m = 88600.980.95256050421643921.0
m = 88610.950.90728357529205280.9927164247079471
m = 88620.970.93656551904362821.0
m = 88630.990.97049860458201211.0
m = 88640.920.86682751000723330.9731724899927667
m = 88650.980.95256050421643921.0
m = 88660.970.93656551904362821.0
m = 88670.950.90728357529205280.9927164247079471
m = 88680.940.89345343433859490.986546565661405
m = 88690.90.84120108046379840.9587989195362017
m = 88700.930.87999210370079670.9800078962992034
m = 88710.940.89345343433859490.986546565661405
m = 88720.880.81630870927157310.943691290728427
m = 88730.880.81630870927157310.943691290728427
m = 88740.840.76814653345166350.9118534665483364
m = 88750.920.86682751000723330.9731724899927667
m = 88760.890.82867473452597570.9513252654740243
m = 88770.880.81630870927157310.943691290728427
m = 88780.810.73311043552569530.8868895644743048
m = 88790.810.73311043552569530.8868895644743048
m = 88800.820.74470064263657660.8952993573634233
m = 88810.810.73311043552569530.8868895644743048
m = 88820.70.61018316681457940.7898168331854205
m = 88830.750.66513106994428710.8348689300557129
m = 88840.190.113110435525695290.26688956447430473
m = 88850.140.071991791523995240.2080082084760048
m = 88860.150.080015287409427660.21998471259057234
m = 88870.170.096377324172511820.2436226758274882
m = 88880.070.0199921037007966470.12000789629920336
m = 88890.080.0268275100072332750.13317248999276673
m = 88900.080.0268275100072332750.13317248999276673
m = 88910.020.00.04743949578356076
m = 88920.010.00.02950139541798788
m = 88930.010.00.02950139541798788
m = 88940.040.00159270658915751370.07840729341084249
m = 88950.050.0072835752920529450.09271642470794705
m = 88960.010.00.02950139541798788
m = 88970.080.0268275100072332750.13317248999276673
m = 88980.090.033909405653456560.14609059434654342
m = 1616300.990.97049860458201211.0
m = 1616310.970.93656551904362821.0
m = 1616320.970.93656551904362821.0
m = 1616330.980.95256050421643921.0
m = 1616340.990.97049860458201211.0
m = 1616350.990.97049860458201211.0
m = 1616360.950.90728357529205280.9927164247079471
m = 1616370.970.93656551904362821.0
m = 1616380.970.93656551904362821.0
m = 1616390.970.93656551904362821.0
m = 1616400.980.95256050421643921.0
m = 1616410.960.92159270658915750.9984072934108424
m = 1616420.960.92159270658915750.9984072934108424
m = 1616430.980.95256050421643921.0
m = 1616440.960.92159270658915750.9984072934108424
m = 1616450.940.89345343433859490.986546565661405
m = 1616460.960.92159270658915750.9984072934108424
m = 1616470.980.95256050421643921.0
m = 1616480.970.93656551904362821.0
m = 1616490.960.92159270658915750.9984072934108424
m = 1616501.01.01.0
m = 1616510.970.93656551904362821.0
m = 1616521.01.01.0
m = 1616530.990.97049860458201211.0
m = 1616540.970.93656551904362821.0
m = 1616550.980.95256050421643921.0
m = 1616560.990.97049860458201211.0
m = 1616570.980.95256050421643921.0
m = 1616581.01.01.0
m = 1616591.01.01.0
m = 1616601.01.01.0
m = 1616611.01.01.0
m = 1616620.990.97049860458201211.0
m = 1616630.990.97049860458201211.0
m = 1616640.980.95256050421643921.0
m = 1616650.990.97049860458201211.0
m = 1616660.990.97049860458201211.0
m = 1616670.980.95256050421643921.0
m = 1616680.980.95256050421643921.0
m = 1616690.960.92159270658915750.9984072934108424
m = 1616700.980.95256050421643921.0
m = 1616710.960.92159270658915750.9984072934108424
m = 1616720.980.95256050421643921.0
m = 1616730.940.89345343433859490.986546565661405
m = 1616740.980.95256050421643921.0
m = 1616750.940.89345343433859490.986546565661405
m = 1616760.980.95256050421643921.0
m = 1616770.970.93656551904362821.0
m = 1616780.980.95256050421643921.0
m = 1616790.970.93656551904362821.0
m = 1616800.970.93656551904362821.0
m = 1616810.960.92159270658915750.9984072934108424
m = 1616820.960.92159270658915750.9984072934108424
m = 1616831.01.01.0
m = 1616840.980.95256050421643921.0
m = 1616850.980.95256050421643921.0
m = 1616860.750.66513106994428710.8348689300557129
m = 1616870.790.71016905247003790.8698309475299622
m = 1616880.740.65402926793951620.8259707320604838
m = 1616890.60.50398176647289370.6960182335271062
m = 1616900.850.78001528740942760.9199847125905724
m = 1616910.050.0072835752920529450.09271642470794705
m = 1616920.060.0134534343385949930.106546565661405
m = 1616930.050.0072835752920529450.09271642470794705
m = 1616940.040.00159270658915751370.07840729341084249
m = 1616950.020.00.04743949578356076
m = 1616960.170.096377324172511820.2436226758274882
m = 1616970.050.0072835752920529450.09271642470794705
m = 1616980.090.033909405653456560.14609059434654342
m = 2424300.990.97049860458201211.0
m = 2424311.01.01.0
m = 2424321.01.01.0
m = 2424331.01.01.0
m = 2424340.990.97049860458201211.0
m = 2424351.01.01.0
m = 2424361.01.01.0
m = 2424371.01.01.0
m = 2424380.990.97049860458201211.0
m = 2424391.01.01.0
m = 2424400.990.97049860458201211.0
m = 2424411.01.01.0
m = 2424421.01.01.0
m = 2424431.01.01.0
m = 2424441.01.01.0
m = 2424451.01.01.0
m = 2424461.01.01.0
m = 2424471.01.01.0
m = 2424481.01.01.0
m = 2424491.01.01.0
m = 2424500.990.97049860458201211.0
m = 2424511.01.01.0
m = 2424521.01.01.0
m = 2424531.01.01.0
m = 2424541.01.01.0
m = 2424551.01.01.0
m = 2424561.01.01.0
m = 2424571.01.01.0
m = 2424581.01.01.0
m = 2424590.990.97049860458201211.0
m = 2424601.01.01.0
m = 2424611.01.01.0
m = 2424621.01.01.0
m = 2424631.01.01.0
m = 2424641.01.01.0
m = 2424650.990.97049860458201211.0
m = 2424661.01.01.0
m = 2424671.01.01.0
m = 2424681.01.01.0
m = 2424691.01.01.0
m = 2424700.990.97049860458201211.0
m = 2424710.990.97049860458201211.0
m = 2424721.01.01.0
m = 2424730.990.97049860458201211.0
m = 2424741.01.01.0
m = 2424751.01.01.0
m = 2424761.01.01.0
m = 2424771.01.01.0
m = 2424781.01.01.0
m = 2424791.01.01.0
m = 2424800.990.97049860458201211.0
m = 2424811.01.01.0
m = 2424821.01.01.0
m = 2424830.990.97049860458201211.0
m = 2424841.01.01.0
m = 2424850.980.95256050421643921.0
m = 2424860.990.97049860458201211.0
m = 2424870.950.90728357529205280.9927164247079471
m = 2424881.01.01.0
m = 2424890.980.95256050421643921.0
m = 2424900.970.93656551904362821.0
m = 2424910.950.90728357529205280.9927164247079471
m = 2424920.520.42208023071691530.6179197692830847
m = 2424930.110.048674734525975750.17132526547402424
m = 2424940.070.0199921037007966470.12000789629920336
m = 2424950.130.064085738066750260.19591426193324973
m = 2424960.140.071991791523995240.2080082084760048
m = 2424970.090.033909405653456560.14609059434654342
m = 2424980.170.096377324172511820.2436226758274882
m = 3232301.01.01.0
m = 3232310.970.93656551904362821.0
m = 3232321.01.01.0
m = 3232330.970.93656551904362821.0
m = 3232340.990.97049860458201211.0
m = 3232350.990.97049860458201211.0
m = 3232360.990.97049860458201211.0
m = 3232370.980.95256050421643921.0
m = 3232381.01.01.0
m = 3232391.01.01.0
m = 3232401.01.01.0
m = 3232411.01.01.0
m = 3232421.01.01.0
m = 3232431.01.01.0
m = 3232441.01.01.0
m = 3232450.990.97049860458201211.0
m = 3232461.01.01.0
m = 3232470.990.97049860458201211.0
m = 3232480.980.95256050421643921.0
m = 3232490.980.95256050421643921.0
m = 3232501.01.01.0
m = 3232510.980.95256050421643921.0
m = 3232520.990.97049860458201211.0
m = 3232530.980.95256050421643921.0
m = 3232540.980.95256050421643921.0
m = 3232550.980.95256050421643921.0
m = 3232560.990.97049860458201211.0
m = 3232570.980.95256050421643921.0
m = 3232580.990.97049860458201211.0
m = 3232590.990.97049860458201211.0
m = 3232601.01.01.0
m = 3232610.990.97049860458201211.0
m = 3232620.990.97049860458201211.0
m = 3232630.980.95256050421643921.0
m = 3232640.990.97049860458201211.0
m = 3232651.01.01.0
m = 3232661.01.01.0
m = 3232671.01.01.0
m = 3232681.01.01.0
m = 3232690.990.97049860458201211.0
m = 3232701.01.01.0
m = 3232711.01.01.0
m = 3232721.01.01.0
m = 3232730.990.97049860458201211.0
m = 3232740.980.95256050421643921.0
m = 3232750.950.90728357529205280.9927164247079471
m = 3232760.990.97049860458201211.0
m = 3232770.990.97049860458201211.0
m = 3232780.980.95256050421643921.0
m = 3232791.01.01.0
m = 3232800.990.97049860458201211.0
m = 3232810.990.97049860458201211.0
m = 3232820.990.97049860458201211.0
m = 3232830.920.86682751000723330.9731724899927667
m = 3232840.980.95256050421643921.0
m = 3232850.990.97049860458201211.0
m = 3232860.970.93656551904362821.0
m = 3232870.960.92159270658915750.9984072934108424
m = 3232880.970.93656551904362821.0
m = 3232890.980.95256050421643921.0
m = 3232900.970.93656551904362821.0
m = 3232910.960.92159270658915750.9984072934108424
m = 3232920.920.86682751000723330.9731724899927667
m = 3232930.950.90728357529205280.9927164247079471
m = 3232940.810.73311043552569530.8868895644743048
m = 3232950.880.81630870927157310.943691290728427
m = 3232960.530.4321783565750960.6278216434249041
m = 3232970.010.00.02950139541798788
m = 3232980.070.0199921037007966470.12000789629920336
Historical precision@10 curves for 10,000, 20,000, and 30,000 points. All three curves drop at similar removal thresholds.
The three collection sizes reached similar accuracy thresholds in the 2019 experiment.
Historical source data
SeriesExperiment parameterVariable parameterprecision@10Interval lowInterval highAverage position
10,000 points10000300.980.95256050421643921.0
10,000 points10000310.960.92159270658915750.9984072934108424
10,000 points10000321.01.01.0
10,000 points10000330.980.95256050421643921.0
10,000 points10000340.980.95256050421643921.0
10,000 points10000350.980.95256050421643921.0
10,000 points10000360.990.97049860458201211.0
10,000 points10000370.940.89345343433859490.986546565661405
10,000 points10000380.980.95256050421643921.0
10,000 points10000390.990.97049860458201211.0
10,000 points10000400.920.86682751000723330.9731724899927667
10,000 points10000410.970.93656551904362821.0
10,000 points10000421.01.01.0
10,000 points10000431.01.01.0
10,000 points10000440.980.95256050421643921.0
10,000 points10000450.970.93656551904362821.0
10,000 points10000460.970.93656551904362821.0
10,000 points10000470.990.97049860458201211.0
10,000 points10000480.970.93656551904362821.0
10,000 points10000490.960.92159270658915750.9984072934108424
10,000 points10000500.960.92159270658915750.9984072934108424
10,000 points10000510.950.90728357529205280.9927164247079471
10,000 points10000520.940.89345343433859490.986546565661405
10,000 points10000530.980.95256050421643921.0
10,000 points10000540.970.93656551904362821.0
10,000 points10000550.980.95256050421643921.0
10,000 points10000560.970.93656551904362821.0
10,000 points10000570.990.97049860458201211.0
10,000 points10000580.980.95256050421643921.0
10,000 points10000591.01.01.0
10,000 points10000600.970.93656551904362821.0
10,000 points10000610.990.97049860458201211.0
10,000 points10000620.990.97049860458201211.0
10,000 points10000630.980.95256050421643921.0
10,000 points10000640.980.95256050421643921.0
10,000 points10000650.950.90728357529205280.9927164247079471
10,000 points10000660.980.95256050421643921.0
10,000 points10000670.960.92159270658915750.9984072934108424
10,000 points10000680.930.87999210370079670.9800078962992034
10,000 points10000690.970.93656551904362821.0
10,000 points10000700.980.95256050421643921.0
10,000 points10000710.970.93656551904362821.0
10,000 points10000720.980.95256050421643921.0
10,000 points10000730.980.95256050421643921.0
10,000 points10000740.940.89345343433859490.986546565661405
10,000 points10000750.940.89345343433859490.986546565661405
10,000 points10000760.980.95256050421643921.0
10,000 points10000770.990.97049860458201211.0
10,000 points10000780.960.92159270658915750.9984072934108424
10,000 points10000790.940.89345343433859490.986546565661405
10,000 points10000800.90.84120108046379840.9587989195362017
10,000 points10000810.930.87999210370079670.9800078962992034
10,000 points10000820.990.97049860458201211.0
10,000 points10000830.960.92159270658915750.9984072934108424
10,000 points10000840.950.90728357529205280.9927164247079471
10,000 points10000850.930.87999210370079670.9800078962992034
10,000 points10000860.910.85390940565345660.9660905943465434
10,000 points10000870.920.86682751000723330.9731724899927667
10,000 points10000880.940.89345343433859490.986546565661405
10,000 points10000890.920.86682751000723330.9731724899927667
10,000 points10000900.850.78001528740942760.9199847125905724
10,000 points10000910.570.472966935689316140.6670330643106838
10,000 points10000920.590.49360244356178370.6863975564382162
10,000 points10000930.190.113110435525695290.26688956447430473
10,000 points10000940.080.0268275100072332750.13317248999276673
10,000 points10000950.090.033909405653456560.14609059434654342
10,000 points10000960.080.0268275100072332750.13317248999276673
10,000 points10000970.030.00.06343448095637183
10,000 points10000980.120.0563087092715730960.18369129072842688
20,000 points20000300.930.87999210370079670.9800078962992034
20,000 points20000310.960.92159270658915750.9984072934108424
20,000 points20000320.960.92159270658915750.9984072934108424
20,000 points20000330.930.87999210370079670.9800078962992034
20,000 points20000340.930.87999210370079670.9800078962992034
20,000 points20000350.970.93656551904362821.0
20,000 points20000360.960.92159270658915750.9984072934108424
20,000 points20000370.940.89345343433859490.986546565661405
20,000 points20000380.970.93656551904362821.0
20,000 points20000390.930.87999210370079670.9800078962992034
20,000 points20000400.950.90728357529205280.9927164247079471
20,000 points20000410.990.97049860458201211.0
20,000 points20000420.950.90728357529205280.9927164247079471
20,000 points20000430.950.90728357529205280.9927164247079471
20,000 points20000440.920.86682751000723330.9731724899927667
20,000 points20000450.980.95256050421643921.0
20,000 points20000460.960.92159270658915750.9984072934108424
20,000 points20000470.930.87999210370079670.9800078962992034
20,000 points20000480.980.95256050421643921.0
20,000 points20000490.980.95256050421643921.0
20,000 points20000500.950.90728357529205280.9927164247079471
20,000 points20000510.960.92159270658915750.9984072934108424
20,000 points20000520.930.87999210370079670.9800078962992034
20,000 points20000530.970.93656551904362821.0
20,000 points20000540.910.85390940565345660.9660905943465434
20,000 points20000550.990.97049860458201211.0
20,000 points20000560.980.95256050421643921.0
20,000 points20000570.990.97049860458201211.0
20,000 points20000580.980.95256050421643921.0
20,000 points20000590.970.93656551904362821.0
20,000 points20000600.990.97049860458201211.0
20,000 points20000610.970.93656551904362821.0
20,000 points20000621.01.01.0
20,000 points20000630.960.92159270658915750.9984072934108424
20,000 points20000640.970.93656551904362821.0
20,000 points20000650.990.97049860458201211.0
20,000 points20000660.980.95256050421643921.0
20,000 points20000670.970.93656551904362821.0
20,000 points20000680.960.92159270658915750.9984072934108424
20,000 points20000690.970.93656551904362821.0
20,000 points20000700.980.95256050421643921.0
20,000 points20000710.950.90728357529205280.9927164247079471
20,000 points20000721.01.01.0
20,000 points20000730.950.90728357529205280.9927164247079471
20,000 points20000740.970.93656551904362821.0
20,000 points20000750.930.87999210370079670.9800078962992034
20,000 points20000760.950.90728357529205280.9927164247079471
20,000 points20000770.960.92159270658915750.9984072934108424
20,000 points20000780.930.87999210370079670.9800078962992034
20,000 points20000790.950.90728357529205280.9927164247079471
20,000 points20000800.880.81630870927157310.943691290728427
20,000 points20000810.960.92159270658915750.9984072934108424
20,000 points20000820.880.81630870927157310.943691290728427
20,000 points20000830.930.87999210370079670.9800078962992034
20,000 points20000840.930.87999210370079670.9800078962992034
20,000 points20000850.930.87999210370079670.9800078962992034
20,000 points20000860.910.85390940565345660.9660905943465434
20,000 points20000870.910.85390940565345660.9660905943465434
20,000 points20000880.60.50398176647289370.6960182335271062
20,000 points20000890.490.39202140237319870.5879785976268013
20,000 points20000900.420.32326431016832050.5167356898316795
20,000 points20000910.350.256515676089094260.4434843239109057
20,000 points20000920.30.21018316681457940.3898168331854206
20,000 points20000930.20.121601440618397820.2783985593816022
20,000 points20000940.040.00159270658915751370.07840729341084249
20,000 points20000950.050.0072835752920529450.09271642470794705
20,000 points20000960.080.0268275100072332750.13317248999276673
20,000 points20000970.070.0199921037007966470.12000789629920336
20,000 points20000980.050.0072835752920529450.09271642470794705
30,000 points30000300.960.92159270658915750.9984072934108424
30,000 points30000310.960.92159270658915750.9984072934108424
30,000 points30000320.940.89345343433859490.986546565661405
30,000 points30000330.950.90728357529205280.9927164247079471
30,000 points30000340.940.89345343433859490.986546565661405
30,000 points30000350.970.93656551904362821.0
30,000 points30000360.950.90728357529205280.9927164247079471
30,000 points30000370.980.95256050421643921.0
30,000 points30000380.920.86682751000723330.9731724899927667
30,000 points30000390.960.92159270658915750.9984072934108424
30,000 points30000400.960.92159270658915750.9984072934108424
30,000 points30000410.980.95256050421643921.0
30,000 points30000420.960.92159270658915750.9984072934108424
30,000 points30000430.930.87999210370079670.9800078962992034
30,000 points30000440.990.97049860458201211.0
30,000 points30000450.960.92159270658915750.9984072934108424
30,000 points30000460.940.89345343433859490.986546565661405
30,000 points30000470.990.97049860458201211.0
30,000 points30000480.960.92159270658915750.9984072934108424
30,000 points30000490.940.89345343433859490.986546565661405
30,000 points30000500.980.95256050421643921.0
30,000 points30000510.970.93656551904362821.0
30,000 points30000520.970.93656551904362821.0
30,000 points30000531.01.01.0
30,000 points30000540.960.92159270658915750.9984072934108424
30,000 points30000550.970.93656551904362821.0
30,000 points30000560.980.95256050421643921.0
30,000 points30000570.950.90728357529205280.9927164247079471
30,000 points30000580.980.95256050421643921.0
30,000 points30000590.980.95256050421643921.0
30,000 points30000600.960.92159270658915750.9984072934108424
30,000 points30000610.960.92159270658915750.9984072934108424
30,000 points30000620.970.93656551904362821.0
30,000 points30000630.970.93656551904362821.0
30,000 points30000641.01.01.0
30,000 points30000650.970.93656551904362821.0
30,000 points30000660.980.95256050421643921.0
30,000 points30000670.940.89345343433859490.986546565661405
30,000 points30000680.960.92159270658915750.9984072934108424
30,000 points30000690.980.95256050421643921.0
30,000 points30000700.980.95256050421643921.0
30,000 points30000710.970.93656551904362821.0
30,000 points30000720.950.90728357529205280.9927164247079471
30,000 points30000730.980.95256050421643921.0
30,000 points30000740.980.95256050421643921.0
30,000 points30000750.970.93656551904362821.0
30,000 points30000760.980.95256050421643921.0
30,000 points30000770.970.93656551904362821.0
30,000 points30000780.950.90728357529205280.9927164247079471
30,000 points30000790.950.90728357529205280.9927164247079471
30,000 points30000800.940.89345343433859490.986546565661405
30,000 points30000810.930.87999210370079670.9800078962992034
30,000 points30000820.930.87999210370079670.9800078962992034
30,000 points30000830.850.78001528740942760.9199847125905724
30,000 points30000840.90.84120108046379840.9587989195362017
30,000 points30000850.90.84120108046379840.9587989195362017
30,000 points30000860.860.79199179152399520.9280082084760047
30,000 points30000870.760.6762932446636110.8437067553363891
30,000 points30000880.790.71016905247003790.8698309475299622
30,000 points30000890.710.62106427201732020.7989357279826798
30,000 points30000900.710.62106427201732020.7989357279826798
30,000 points30000910.590.49360244356178370.6863975564382162
30,000 points30000920.420.32326431016832050.5167356898316795
30,000 points30000930.350.256515676089094260.4434843239109057
30,000 points30000940.090.033909405653456560.14609059434654342
30,000 points30000950.120.0563087092715730960.18369129072842688
30,000 points30000960.120.0563087092715730960.18369129072842688
30,000 points30000970.070.0199921037007966470.12000789629920336
30,000 points30000980.10.041201080463798370.15879891953620165

There is a clear threshold when the search begins to fail. This threshold is due to the decomposition of the graph into small connected components. The graphs also show that this threshold can be shifted by increasing the $m$ parameter of the algorithm, which is responsible for the degree of nodes.

Let’s consider some other filtering conditions we might want to apply in the search:

  • Categorical filtering
    • Select only points in a specific category
    • Select points which belong to a specific subset of categories
    • Select points with a specific set of labels
  • Numerical range
  • Selection within some geographical region

In the first case, we can guarantee that the HNSW graph will be connected simply by creating additional edges inside each category separately, using the same graph construction algorithm, and then combining them into the original graph. In this case, the total number of edges will increase by no more than 2 times, regardless of the number of categories.

Second case is a little harder. A connection may be lost between two categories if they lie in different clusters.

A query near category A circles and an entry point in category B squares, with dashed gray nodes in other categories connecting the graph.
Filtering out other categories can remove the connections between category A and category B.

The idea here is to build same navigation graph but not between nodes, but between categories. Distance between two categories might be defined as distance between category entry points (or, for precision, as the average distance between a random sample). Now we can estimate expected graph connectivity by number of excluded categories, not nodes. It still does not guarantee that two random categories will be connected, but allows us to switch to multiple searches in each category if connectivity threshold passed. In some cases, multiple searches can be even faster if you take advantage of parallel processing.

Historical precision@10 falls from 1.0 to 0.96 as the search-group parameter increases, then returns to 1.0. The vertical axis is restricted to show this gap.
The original interpretation attributes the initial accuracy to within-category connectivity, the dip to a connectivity gap, and the recovery to a larger connected component. Increasing m was proposed to narrow that gap.
Historical source data
SeriesExperiment parameterVariable parameterprecision@10Interval lowInterval highAverage position
1,000 groups100001.01.01.00.7161904761904762
1,000 groups100011.01.01.00.7161904761904762
1,000 groups100020.99428571428571430.98972649987228060.99884492869914811.118095238095238
1,000 groups100030.980.97153198775865170.98846801224134831.2742857142857142
1,000 groups100040.96857142857142850.95801829126373970.97912456587911731.5380952380952382
1,000 groups100050.97142857142857140.96135171999504840.98150542286209441.2952380952380953
1,000 groups100060.960.948147251927460.971852748072541.4114285714285715
1,000 groups100070.97714285714285710.96810337723770550.98618233704800870.9647619047619047
1,000 groups100080.98476190476190470.97735248152318840.9921713280006210.679047619047619
1,000 groups100090.980.97153198775865170.98846801224134830.7428571428571429
1,000 groups1000100.97238095238095230.96246861643931720.98229328832258740.9285714285714286
1,000 groups1000110.96761904761904760.95691247529846360.97832561993963160.9247619047619048
1,000 groups1000120.98380952380952380.97617575920518080.99144328841386680.5171428571428571
1,000 groups1000130.98761904761904760.98093060205528530.994307493182810.5438095238095239
1,000 groups1000141.01.01.00.16476190476190475
1,000 groups1000151.01.01.00.16476190476190475
1,000 groups1000161.01.01.00.16476190476190475
1,000 groups1000171.01.01.00.16476190476190475
1,000 groups1000181.01.01.00.16476190476190475

These charts use the original recorded observations. Their vertical axis preserves the original precision@10 metric; it does not directly measure graph connectivity. The expandable tables retain the recorded intervals and any average-position values. The diagrams adapt the original illustrations.

Third case might be resolved in a same way it is resolved in classical databases. Depending on labeled subsets size ration we can go for one of the following scenarios:

  • if at least one subset is small: perform search over the label containing smallest subset and then filter points consequently.
  • if large subsets give large intersection: perform regular search with constraints expecting that intersection size fits connectivity threshold.
  • if large subsets give small intersection: perform linear search over intersection expecting that it is small enough to fit a time frame.

Numerical range case can be reduces to the previous one if we split numerical range into a buckets containing equal amount of points. Next we also connect neighboring buckets to achieve graph connectivity. We still need to filter some results which presence in border buckets but do not fulfill actual constraints, but their amount might be regulated by the size of buckets.

Geographical case is a lot like a numerical one. Usual geographical search involves geohash, which matches any geo-point to a fixes length identifier.

Original geohash map with street geography, labeled cells, neighboring cells, and the selected region.
The original map groups locations into geohash cells. Neighboring cells provide connections around the selected region.

We can use this identifiers as categories and additionally make connections between neighboring geohashes. It will ensure that any selected geographical region will also contain connected HNSW graph.

Conclusion

It is possible to enchant HNSW algorithm so that it will support filtering points in a first search phase. Filtering can be carried out on the basis of belonging to categories, which in turn is generalized to such popular cases as numerical ranges and geo.

Experiments were carried by modification python implementation of the algorithm, but real production systems require much faster version, like NMSLib.

Was this page useful?

Thank you for your feedback! 🙏

We are sorry to hear that. 😔 You can edit this page on GitHub, or create a GitHub issue.