From 907c83e4cbaf38300bb96c804ccd9e9dff860a2e Mon Sep 17 00:00:00 2001 From: Rahul Rampure Date: Wed, 8 Nov 2023 22:50:16 +0530 Subject: [PATCH 1/5] Added a document that demonstrates the performance benefits of docvalues (#1897) sort_facet.md displays which query configuration utilizes docvalues and, through a test scenario, illustrates the trade-off between the increase in index size and the performance advantage gained in return. --------- Co-authored-by: Abhinav Dangeti --- docs/sort_facet.md | 786 ++++++++++++++++++ .../indexSizeVsNumDocs.png | Bin 0 -> 30918 bytes .../queryTimevsNumDocs.png | Bin 0 -> 32162 bytes 3 files changed, 786 insertions(+) create mode 100644 docs/sort_facet.md create mode 100644 docs/sort_facet_supporting_docs/indexSizeVsNumDocs.png create mode 100644 docs/sort_facet_supporting_docs/queryTimevsNumDocs.png diff --git a/docs/sort_facet.md b/docs/sort_facet.md new file mode 100644 index 000000000..14ea5c6f4 --- /dev/null +++ b/docs/sort_facet.md @@ -0,0 +1,786 @@ +

Purpose of Docvalues

+ +

Background

+ +

What are docValues? In the index mapping, there is an option to enable or disable docValues for a specific field mapping. However, what does it actually mean to activate or deactivate docValues, and how does it impact the end user? This document aims to address these questions.

+
+	"default_mapping": {
+		"dynamic": true,
+		"enabled": true,
+		"properties": {
+			"loremIpsum": {
+			"enabled": true,
+			"dynamic": false,
+			"fields": [
+				{
+					"name": "loremIpsum",
+					"type": "text",
+					"store": false,
+					"index": true,
+					"include_term_vectors": false,
+					"include_in_all": false,
+					"docvalues": true
+				}
+			]
+		}
+	}
+
+

Enabling docValues will always result in an increase in the size of your Bleve index, leading to a corresponding increase in disk usage. But what advantages can you expect in return? This document also quantitatively assesses this trade-off with a test case.

+ +

In a more general sense, we recommend enabling docValues on a field mapping if you anticipate queries that involve sorting and/or facet operations on that field. It's important to note, though, that sorting and faceting will work irrespective of whether docValues are enabled or not. This may lead you to wonder if there's any real benefit to enabling docValues since you're allocating extra disk space without an apparent return. The real advantage, however, becomes evident in enhanced query response times and reduced memory consumption during active usage. By accepting a minor increase in the disk space used by your Full-Text Search (FTS) index, you can anticipate better performance in handling search requests that involve sorting and faceting.

+ +

Usage

+ +

The initial use of docValues comes into play when sorting is involved. In the search request JSON, there is a field named "sort." This optional "sort" field can have a slice of JSON objects as its value. Each JSON object must belong to one of the following types: +

+

+

DocValues are relevant only when any of the JSON objects in the "sort" field are of type SortGeoDistance or SortField. This means that if you expect queries on a field F, where the queries either do not specify a value for the "sort" field or provide a JSON object of type SortDocID or SortScore, enabling docValues will not improve sorting operations, and as a result, query latency will remain unchanged. It's worth noting that the default sorting object, SortScore, does not require docValues to be enabled for any of the field mappings. Therefore, a search request without a sorting operation will not utilize docValues at all.

+
+ + + + + + + + + + + + + + + + + + + + + + +
No Sort ObjectsSortDocIDSortScoreSortFieldSortGeoDistance
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field":"dolor"
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field":"sit_amet"
+  },
+  "sort":[
+    {
+     "by":"id",
+     "desc":true
+    }
+    ],
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field":"sit_amet"
+  },
+  "sort":[
+    {
+     "by":"score",
+    }
+    ],
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field":"sit_amet"
+  },
+  "sort":[
+    {
+     "by":"field",
+     "field":"dolor",
+     "type":"auto",
+     "mode":"min",
+     "missing":"last"
+    }
+    ],
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "dolor"
+  },
+  "sort": [
+    {
+      "by": "geo_distance",
+      "field": "sit_amet",
+      "location": [
+        123.223,
+        34.33
+      ],
+      "unit": "km"
+    }
+  ],
+  "size": 10,
+  "from": 0
+}
+			
+
No DocValues usedNo DocValues usedNo DocValues usedDocValues used for field "dolor". Field Mapping for "dolor" may enable docValues.DocValues used, for field "sit_amet". +Field Mapping for "sit_amet" may enable docValues.
+
+

Now, let's consider faceting. The search request object also includes another field called "facets," where you can specify a collection of facet requests, with each request being associated with a unique name. Each of these facet requests can fall into one of three types: +

+Enabling docValues for the fields associated with such facet requests might provide benefits in this context.

+
+ + + + + + + + + + + + + + + + + +
No Facet RequestDate Range FacetNumeric Range FacetTerm Facet
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "dolor"
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "sit_amet"
+  },
+  "facet": {
+    "facetA": {
+      "size": 1,
+      "field": "dolor",
+      "date_ranges": [
+        {
+          "name": "lorem",
+          "start": "20/August/2001",
+          "end": "22/August/2002",
+          "datetime_parser": "custDT"
+        }
+      ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "sit_amet"
+  },
+  "facet": {
+    "facetA": {
+      "size": 1,
+      "field": "dolor",
+      "numeric_ranges":[
+          { 
+            "name":"lorem",
+            "min":22,
+            "max":34
+          }
+        ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "sit_amet"
+  },
+  "facet": {
+    "facetA": {
+      "size": 1,
+      "field": "dolor"
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
No DocValues usedDocValues used for field "dolor". Field Mapping for "dolor" may enable docValues.
+
+ +

In summary, when a search request is received by the Bleve index, it extracts all the fields from the sort objects and facet objects. To potentially benefit from docValues, you should consider enabling docValues for the fields mentioned in SortField and SortGeoDistance sort objects, as well as the fields associated with all the facet objects. By doing so, you can optimize sorting and faceting operations in your search queries.

+ +
+ + + + + + + + + + + + + +
Combo ACombo B
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "sit_amet"
+  },
+  "facet": {
+    "facetA": {
+      "size": 1,
+      "field": "dolor",
+      "date_ranges": [
+        {
+          "name": "lorem",
+          "start": "20/August/2001",
+          "end": "22/August/2002",
+          "datetime_parser": "custDT"
+        }
+      ]
+    }
+  },
+  "sort":[
+    {
+     "by":"field",
+     "field":"lorem",
+     "type":"auto",
+     "mode":"min",
+     "missing":"last"
+    }
+    ],
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "lorem ipsum",
+    "field": "sit_amet"
+  },
+  "facet": {
+    "facetA": {
+      "size": 1,
+      "field": "dolor",
+      "numeric_ranges":[
+          { 
+            "name":"lorem",
+            "min":22,
+            "max":34
+          }
+        ]
+    }
+  },
+  "sort": [
+    {
+      "by": "geo_distance",
+      "field": "ipsum",
+      "location": [
+        123.223,
+        34.33
+      ],
+      "unit": "km"
+    }
+  ],
+  "size": 10,
+  "from": 0
+}
+			
+
DocValues used for field "dolor" and "lorem". Field Mapping for "dolor" and "lorem" may enable docValues.DocValues used for field "dolor" and "ipsum". Field Mapping for "dolor" and "ipsum" may enable docValues.
+
+ +

Empirical Analysis

+ +

To evaluate our hypothesis, I've set up a sample dataset on my personal computer and I've created two Bleve indexes: one with docvalues enabled for three fields (dummyDate, dummyNumber, and dummyTerm), and another where I've disabled docValues for the same three fields. These field mappings were incorporated into the Default Mapping. It's important to mention that for both indexes, DocValues for dynamic fields were enabled, as the default mapping is dynamic.

+ +

The values for dummyDate and dummyNumber were configured to increase monotonically, with dummyDate representing a date value and `dummyNumber` representing a numeric value. This setup was intentional to ensure that facet aggregation would consistently result in cache hits and misses, providing a useful testing scenario.

+ +
+ + + + + + + + + + + + + +
Index AIndex B
+
+   "default_mapping": {
+    "dynamic": true,
+    "enabled": true,
+    "properties": {
+     "dummyNumber": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyNumber",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": true
+       }
+      ]
+     },
+     "dummyTerm": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyTerm",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": true
+       }
+      ]
+     },
+     "dummyDate": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyDate",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": true
+       }
+      ]
+     }
+    }
+   }
+			
+
+
+   "default_mapping": {
+    "dynamic": true,
+    "enabled": true,
+    "properties": {
+     "dummyNumber": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyNumber",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": false
+       }
+      ]
+     },
+     "dummyTerm": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyTerm",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": false
+       }
+      ]
+     },
+     "dummyDate": {
+      "enabled": true,
+      "dynamic": false,
+      "fields": [
+       {
+        "name": "dummyDate",
+        "type": "text",
+        "store": false,
+        "index": true,
+        "include_term_vectors": false,
+        "include_in_all": false,
+        "docvalues": false
+       }
+      ]
+     }
+    }
+   }
+			
+
Docvalues enabled across all three field mappingsDocvalues disabled across all three field mappings
+
+ +Document Format used for the test scenario: + +
+ + + + + + + + + + + + +
Document 1Document 2... Document iDocument 5000
+
+{
+	"dummyTerm":"Term",
+	"dummyDate":"2000-01-01T00:00:00,
+	"dummyNumber:1
+}
+			
+
+
+{
+	"dummyTerm":"Term",
+	"dummyDate":"2000-01-01T01:00:00,
+	"dummyNumber:2
+}
+			
+
+
+{
+	"dummyTerm":"Term",
+	"dummyDate":"2000-01-01T01:00:00"+(i hours),
+	"dummyNumber:i
+}
+			
+
+
+{
+	"dummyTerm":"Term",
+	"dummyDate":2000-01-01T01:00:00 + (5000 hours),
+	"dummyNumber:5000
+}
+			
+
+
+ +

Now I ran the following set of search requests across both the indexes, while increasing the number of documents indexed from 2000 to 4000.

+ +
+ + + + + + + + + + + + +
Request 1Request 2... Request iRequest 1000
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "term",
+    "field":"dummyTerm"
+  },
+  "facets":{
+    "myDate":{
+      "field":"dummyDate",
+      "size":100000,
+      "date_ranges":[
+        {
+          "start":"2000-01-01T00:00:00",
+          "end":"2000-01-01T01:00:00"
+        }
+      ]
+    },
+    "myNum":{
+      "field":"dummyNumber",
+      "size":100000,
+      "numeric_ranges":[
+        {
+          "min": 1000,
+          "max": 1001
+        }
+      ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "term",
+    "field":"dummyTerm"
+  },
+  "facets":{
+    "myDate":{
+      "field":"dummyDate",
+      "size":100000,
+      "date_ranges":[
+        {
+          "start":"2000-01-01T01:00:00",
+          "end":"2000-01-01T02:00:00"
+        }
+      ]
+    },
+    "myNum":{
+      "field":"dummyNumber",
+      "size":100000,
+      "numeric_ranges":[
+        {
+          "min": 999,
+          "max": 1000
+        }
+      ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "term",
+    "field":"dummyTerm"
+  },
+  "facets":{
+    "myDate":{
+      "field":"dummyDate",
+      "size":100000,
+      "date_ranges":[
+        {
+          "start":"2000-01-01T00:00:00" + i hour
+          "end":"2000-01-01T00:00:00" + (i+1) hour
+        }
+      ]
+    },
+    "myNum":{
+      "field":"dummyNumber",
+      "size":100000,
+      "numeric_ranges":[
+        {
+          "min": 1000-i,
+          "max": 1000-i+1
+        }
+      ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+{
+  "explain": true,
+  "fields": [
+    "*"
+  ],
+  "highlight": {},
+  "query": {
+    "match": "term",
+    "field":"dummyTerm"
+  },
+  "facets":{
+    "myDate":{
+      "field":"dummyDate",
+      "size":100000,
+      "date_ranges":[
+        {
+          "start":"2000-01-01T01:00:00" + 1000 hour,
+          "end":"2000-01-01T02:00:00" + 1001 hour
+        }
+      ]
+    },
+    "myNum":{
+      "field":"dummyNumber",
+      "size":100000,
+      "numeric_ranges":[
+        {
+          "min": 0,
+          "max": 1
+        }
+      ]
+    }
+  },
+  "size": 10,
+  "from": 0
+}
+			
+
+
+ + +
+ + + + + + + + +
Bleve index size growth with increase in indexed documentsTotal query time for 1000 queries with increase in number of indexed documents
indexSizeVsNumDocs.pngqueryTimevsNumDocs.png
+
+ +
+ + + + + + + + + +
Average increase in index size (in bytes) by enabling DocValuesAverage reduction in time taken to perform 1000 queries (in milliseconds) by enabling DocValues
7762.4727.034
+Even at this small scale, with a small document size and a very limited number of indexed documents, we still observe a noticeable tradeoff. With just a slight increase in the index size (an average of 7KB) we obtain a 20ms reduction in the total execution time, on average, for only 1000 queries. + +

Technical Information

+ +

When a search request involves facet or sorting operations on a field F, these operations occur after the main search query is executed. For instance, if the main query yields a result of 200 documents, the sorting and faceting processes will be applied to these 200 documents. However, the main query result only provides a set of document IDs, not the actual document contents.

+ +

Here's where docValues become essential. If the field mapping for F is docValue enabled, the system can directly access the values for the field from the stored docValue part in the index file. This means that for each document ID returned in the search result, the field values are readily available.

+ +

However, if docValues are not enabled for field F, the system must take a different approach. It needs to "fetch the document" from the index file, read the value for field F, and cache this field-document pair in memory for further processing. The issue becomes apparent in the latter scenario. By not enabling docValues for field F, you essentially retrieve all the documents in the search result (at the worst case), which can be a substantial amount of data. Moreover, you have to cache this information in memory, leading to increased memory usage. As a result, query latency significantly suffers because you're essentially fetching and processing all documents, which can be both time-consuming and resource-intensive. Enabling docValues for the relevant fields is, therefore, a crucial optimization to enhance query performance and reduce memory overhead in such situations.

diff --git a/docs/sort_facet_supporting_docs/indexSizeVsNumDocs.png b/docs/sort_facet_supporting_docs/indexSizeVsNumDocs.png new file mode 100644 index 0000000000000000000000000000000000000000..11211709d5faa1e6d27e8a493f49ef532cc56de3 GIT binary patch literal 30918 zcmd?RWmuJMw>CNf0TmDl2`QbFC=Jpf0wPi>N=UazcZVWf0!o*nqN23YA>Ang(m6r8 zC;g4v_xRRYd#~U7*Y4D5sTZuCm6k#=d=6OcwfxoZq#ubBF_NWn%E9;8927 zPixGuvQ2m~Lq1&K~bgWZw=vf3(#tjr{DDJw$fIO~@po zp!}?+Nzw3GBBCsn6rnD&jg5`U{rME{y)if4Qv2tdbL|4QqauF?^GIu-;}L$+DnNVf zt$uJ`?E6j0jqIk3UajSOwG1ii;ljY-B8wugJ==7TdD?XM>2ntt#Lm=CH->X)e5Fx} z=Y0FL_Z=n2!Kxu7{w z%0~>P5Y0H(5K7UJCZ3QaV8L(FMC`ge^s`@0+QwlAv6pAgB!-uViY!Q5TU%jet~YLC zvF?tuEja#Tw(L?;>2Q!(Ns5roWm4%Q-k6w}zf@GnmG8in8=9J?7Z+Qj7{u4Nx0MUH zuUtX-S9)ws&&|C-v&3`hP}vt>xsDiV%keu@BEgqOpKm)l@>_JLe^5;m6CVBOy7GsG zmDORkg)EGk=R)nz079H=ux7~v*Wt!;)w2B8;)aH_E-*SQ6+*&I{o_NFZQdWqQAdt%Ql8xvh00_?^==ecOZx2;>FsR=P2?=R$jh* zd69;urR~iPE-7(j<5u^VA&igEg=?&36b|{f^j}X^xZh+AWr1K+b9Of8Ddc7V+Bu z<(Gma9g=V_M{wt+t+Dtio#rm0@SL&dn`pT8EALi&@2{`hcmLqUuk~8TWcWSIH*47MyEpfH zupJ(N9KV+E+SRMiKYhB!tyga9!hzo7RR5?v+nGYm#Ke@N1h?k(9)*5vNpyOX13PW0lg5wWaFS!jrZj@|G) z`NP&=EmQF$m%u5`QLw#NRMBt*$i|V8l!Kk6 z?4jLf3*wHmXTLuiKwrNemYI14W!{k}1J<*qq@q{u{L{S1sAzb7cb82>BoXW&6*ir` zV$9b-A1@Ok!1nU+R?t%Q$$^!(kI(*6Ud`&rQfNqsmLgp7vf$HO*4CUOC3bGyBl$*n z9-Fg9BR_0(%N!faBqStK;qV8Q?hfAG-Ys$L-FS3!M-3Ee7cX6E1VhrU_7(+e%rvO+ zEw&!!m)EMI9xHb-*CaZfw6bTgSahZC&3%Vm|N0SiP4$CP0-N18;2HSft4ZL{2|v+`w0ekA zmTkJFtNTYfLeVD@7WRsS#OOY<319>2dHRx=&GrPIBAXE*3sj}Xw6&K1}}WEGHD(zw)- z$QOW%f4=9vB89PuiRE~uD16Td0+Pf=14Z$(fv`AswQ+}IO+82@K_ql=)k{C@Cc45d z-4Jn_C+?Oy!CZ!U)$vDUF~pN?mlCC!(tR&GVuHB! zCYzb2M(n=jcF5C~S@rhz67a%rgWoE=_X~$>{Ays&3Z1mAzw$+i#Q%sM8v2#d|Le25 z%6vzXim~xKh>m>v`ug+B!$s!cEFW#h%A06R;BO`^s z17`|KO6Rcx*ih?qzY`CPQ^lCABc{uSO(og6-&4$O)%r<^txvrRtd+1?d;HBNnj*)! zdozoRma6Fzi@G&-6H<7I8NLUtG$zWO$v0J+qZsTCp4`y*$}5!8NiXU7OWRN?;7N=~g;NaP+&xS<*KRl5k;K@_A#`K&vU zww{$p_yOzSh1EpCO4ql>v3qPZQsXrHCl2a+MHZU1$KJHk0A~`hpRASqjygG5%6l0c z42f^j`!$C;fj_fC)cwy7SufvwCI?B=eLl$|QOxB!>`~3qAO%@LGGD&SjqN2u{PVZs z*mzBXvPMb_W!#TlhRP^**Mi^XvEQ#O9k^7%Hg=AM#H{U1LBa1z+h=dCJ$L~(b&-*= zYkv##Su>9j1#v&i@aGHb@yaB~VV8xh-8H|vdwFHM+ZGM4dJ3)q@VWM+`01CDy}iAf zIZskj(sJj;I!yzgMjgAV#_h#^%brXe$lZ98-v{+g2EIK02h*-S!1eRhth(&JBt7x>w zNC|26Vb=zD=4k?^1*cmAuUnGvyH$Ww*Z%$%1BXK|`qRV4*krw=C#e2codih&*~c@g zgczZxJ^mpfXThT$DxW?qvDJ&`G3Zj0K6b8OI?dhO*tjYxy3g&W^ts4&c}GQw%X9#9Gst@KXMu?C&%eJeI|mA@Xd9Z{mu0*@u>9#Q32=W zvq}lKA5khNpKTf5$WelFJv#$!~+d7vW#b)UWb{ z%?Sqhq(e}PmJho~N7oKd@#)cL@+5w<^6bN_&CShD2iul_A4DM@fp=aJ6nqWPukW)u zJLhLABo{q>2gF`ODBeY#JosHn)P^CLng zfI!qB2av9y_0TIuNzX~hN)@gvsZ00bIa`nRw~&noS2C&(8Y|rCo0!M|+vas$)g)FxvZA$0?XRY%r~7BUv4+@Y+8!TS?y}TZ>RJY&it8Pm4Ot|Jk&ESnOTWL-fnQ_FFzNN(-Q8tJ0i;q_bbeSP zSBL!8TWqQRMdO=i`=6SlO{Lh_Sa%4NixV}{Sqjmr1yTmp-qwGLLuY4AlLalo1TQVX zN7_Z^jrH{bwY9g9S_;w_$Kx-QvcVV3j<6d5f0y-rKLAJ@XbfN98I1Bz5axOLQlu`U z6HKV~*XO99sz9Q*K8JKKYi63?TB^Hm$SYvIutr1G4CB6Z-acF+%!3vJgZh~)ZY%&_ z0fxQH$~s3(tX!}PFdYGn@8rWjo+U%mzBy41Z>eEr6RR5^G!XyjQ1<1?O$DWiqVC%# zMoNIp9+r3u;_Lz-u>{b$H@4d ziDC1Io$PH~(^lNIY9dKbU4b)NjDr6P?=t3KQrW@Ws0_jj&bosM*Tr2$Tt}-$HYz=w z5+#QDa5CR>$lCw}Q~chZz))1&&`@g zboa~73Q^6;`7%e*a^KUNHK#kjzXhD04E9=|UC%hJRbUdma3$U$l&V%Sj_m>D7AOQ@ z;oh5fCUY88AAKBAOA~t|=JNLymyQr*h&vD_?L-)mlH{l+ulfZ~wY}~V6sIM$Q*;z~ zRh}}(2M`^2GE5i~*Q(XAN`$&-6xFr+{skt@g(l4x0Bxw&2a#BgmESzvU8$Jbg-1c) z2E_Rv*2A}dzQDKadwyz^&9bDTxoddMsOaOv zsaAF-_q13sZgEmW2$&vgReKLd+m_5BdbGxK(PemVhHiC{e}3zMYD`;LD-Jmp+6)AB`OGTdj{+jf~ z2PzghV`DVK|o(jf33qN9e$Kj$hRMrl;{@|@+vxExX6BN2#PdKIqPdSM}@Novx8 zqE7U}^N9k4j`K%@1U09t3x9l8MHR3)_i=vM2JVf+ZZzw%q2jsqlHsTV0IPs5>Ykd#eK?9aZ4%imwe*QNCmB|P|{QoPE+(OSkSBt!-E z6(E)jU{;Os}jw z-59B6_@Y_zUSCl%;hevel+KhDPOz z753mjOyRPDPiZWYjv(jku8gF$3t11{g8F3WhYg)(ZdtF1ySoSks(0=i(^xF_6}uXv z_2*<N;KBZwLT60h-kz81N%3SmPYt*|U^l4a6$f0t zpwX5qYc*3z7WBfa?teKJ2G^3`)Ewd7d?r%v&7=QPfhjOe+z4yGzESYi{NIzDnwN~I z>*np*tyLd#+tnNkLE_7N^8J~mmiEEpa%aoU`A#J{xtC7bQ22b)DftNL%o-f1ag*S* z;?v$A?n!_Z`raGz5c1N#S=m+X#5>g}C2%9OQ=w_=?YFlB6Zy^Vge>R)w?A|}+_&%~ydAM2uyMqhF*!fG>XNhBw z>JxA`uuJ@nR^8xQKof|$F1J7`Lb#=tWqPFqpPh7fAH^ z$@l9}##7$Vh=^fU5L*&zTK}zM_u$k0t57w!0ww2jxLY;1rX)8SFm@qL>}PA-{dZ@S zQ+8`cD)lNCXl5T&m1lk6;qB>FsI-hT`ITo%!%c=FBxUS?M@Re!GP=?FSrPe>u5s4l@6O3h_`J4YeK2gg@NCG zhk9nK& z?Wb;|pim)A7PeE}`8%)=_#{ctk{qZk6WHG_9|Cwta0Xf-YnsEoH46YLX9x-LdLWy; z2nYa{>Q2~T63}|!kjzjiHZ(T!8P=kZk`Qp&`uch%JjBq-u6XY07pPKu0ENMdHJ=$zc;bCc zhl)+rnWf?Mh|u?t#B+~8O)W<$UffLg)?lul3V@Wz=;-LyH#ZV2e|?Gp5B}if19h@8 z#Hh4xr1bM&Vr0G(QJOUv85Jeu9~u=^Z94{(LMPl&C;WNpXY}9o=A-|w>P^!KKJ&KM z9BcrG*mjdWf&6#|Oh5-%MkHUXP?&%dcXmgJha{dOGp8igyNM}+P4z0 z)@vZjN-f6*%cmwR$s9zdrkWt+RZCT4>u8*gO8K~SPEOtK6&N>t)iQEP+MCswm{_f2 z{rEgd$mS2%puxz8b5Ue(98GqX2HAnt&hR@)0+Uq#@`wX)qdaVqJ5-WTV4R19j`%5{ zzOjvsysF=Q=0!0OO}o-1iT*Lx2-p{}aJ5yhaJ5->6$c8qi|Tu^VT6NgHlv8WKiJ*J z1^sV&6+#6C1xF``YpGH`$v`1r9y8YX1*8N&fOCMimP7ehfy2ta8-oVWdeI+nB%e3d z{)f#-lY!syob|rA$0iF{L`{wp7FJ>y+$wB|HZCp>xkcEq=_HGEB$Fby%AW52V!kt_ z|I6bWm6f{F8Te#nL=PTB1xZ#glvlsHJ6OYhtE^{qW&1vM*mu3Z%piih~v2g|gyySKH46w#2l3O#q80cMwn zID=p$trFWYm$kT%((3mt3q3epsn4StfAH=kB;@;4H5U}x>Lm)c@=a^%zQ8qnQaC)B z{QKMX7bv43INF~ac@SKnU(nV6@9I@9FJ}+090wR&fL&`?m-cwB_6S;mknnJ)AGo%E ziqFBL-b>&nxs|J1_7DJVYH9~kwdqE?m*n{kK*rvHD1&r5%6%b-P=0#>y#X|ICny0k zLI9juTk8gsz^?h7diBphx`~8^-I$G2LpXI2!rUY1q`)JJtvw*gcbI9?`JH$}lNI3!>M^3p&*6^M`e_(wuDMCLG=%%?Zu`&8=KY&$gcy(1)1Q>3hPs?pzwXRRqCy6?- z!4akt)jss#vH*$&oEPrb^2NIUDYjf5t4M%*+yu&Ga&1iwV!GY_AM~_mJU1UbX$f_6n5gx^58)=Sn!|anAC;l4cw{<;R^2b&C2(-5l>sW?Atw-Mr8vrqeIL|0Pxr1| zG>KRoF3LupBJAf);mghsK0o|G8DQtSHf{qfx#7{#kurcA74VTxAOO;qD9Fi`A>97^ z$JNm?m8VY^Tmo)Eg<*VMJXE5Mae}TT~EV6QtRC6fChQM||Fs?`=@{ z{|3OB47es4U>ROwem<{B$4z2mzVn9;P)q3SJ7#Ja~eRVrPuCd|>UTjhvP} zkqwW(JUeCk3!f93M{UzHZ%q`iQ-rAHxf`2xAG(uViV1d>>RV!d$Fu*wPlU9pbHc&{ zG``VKbvF}kt$H1mY`U5=e3MC+SRZrCOgSxOuUa8ZB+w4e>6tK#A)Y_-c-o8m!`fl= zrQy`=j|^4~+jh0}^<*cTvjydM3&L$Fp8RvoZ*_8g6*&~1OF|B4+S(+6p7-aL z$4Q0@e{Rk#Z~DBJ;-b%)@F4s$$#XQVHXNA-d&-g)7UBG|Mm;;z-bkw|dB-n4(~VHXv7ulgCy!pQc6yu=#dIa5 z^UipsM=Nklz@2L(Fgp%bR#gpbvd50EjGj|T5t~1_p&nE;kvlyQgO8o3YyO)mYBVm< zl0No_pVj9+N*NERpo2|lXAxTTvo-riFFaL#^Tm5(`@Fu@tMEjc{NYRmQW}9fQ)(v} zNU#6yo!yz}o*!Jg)ym2r#ES1e>1YxTKz-66kvb%XBCPa|qT9dzAO9DRt;sUX6~34; z{Nx8SgC4qU6vKt0Pmapn2wiTqMlSH*2Non^p`C|sWkl8(y;U`$zOsi8-_olPYkIN0 z{CS;SIQw6BvDX`W)p;qX)7Ikrm_~)|GD0IlhUJuvoyRY z|2_A}=h{-3`p|Uii3{JKY3pW~()=@E7QB4+VbS%58xqCy%&4IC3)x~9*fxDqY!}(w zlo7cDOWMeH$x!K((}a0UHLjs^y4q7Q@t2-EoZQO%od0N^_v*d9yjNJ#O$IVP?3d-; zt&1}|O=``IJMZUc_wPRXXicEj&%F0pBpv(plFd2h-uhr7bxiM31@-#wkMi5C3;zr~ zHfni{=<3ycuVat+z86odueS0Cj4M&a7Cm6jX{VqDwhvav`N~xseLsflnn4r4GmO%{ zNqrxB(VBcIZ+#$hoqaFn3F32{yL%Jsztr<>87QaE=r$fyZ1?}_b+*4 z<3Ig|*21LB(_!)p3@@%HF%K`CiX@$g;Rh1c{~fsh?&Sb$7|}y&sIf!j>+0X$AyL3C z#aod{?@P>|^)qX$ZHheaw^{q2(?C)CR*@n1`G$xiT+!yrSaWsM( z*G2Ps13uf+UehKw@963DX8n)q4ziViC1sZR^ zzG}w-G@5YCD09L9N~47of%N(So+xEKlb35qdMLKzTxA&y3MaJLTqT}t#{AFB*=#$^Blt9^H?Ej=r>E_WdqqRr zORpps%2z183QSwcF@-=t-!F4CgLZT3-@%a3(5u(3g)A&Q1y*iNX$%;#4OX@~FEzWY zv3Fgsuacv@5^~*@v8Wd>ICE9Ek5|xK{)6{aRm~wA!Wkbx&Ze_1GHa&-?mX348_GgR z_U&BJ3`|UF%F4=N!N6yJ@5+d>uxhhSK#|9fwY8~D*Z8vvuK@U14d7yl}y{~Q5c(F+xt=AtxfvTJ; z5(9HzU*;5twgU9g%jOVPXnJYM>oklCqLba8?TPI`QXRV+PQKWoz$2|w zT6xAcOB+3O$s|9QKSI!iMn~67l>-(94k($j9RRgUzZC&EW^UZ;mp%R!MI9-bqYp@W3iOSeOQS~HG9H`9ikZw*+6{_5r zz6T^L^aSDoCPKSe{{H=ozBINRZ_O<47n)!%RI&%Ft$U zoKyQ*mb*hvI7OLL`9m4-X!D5Ypr?`6AcSwxe-ybneEZSJMIpNf*tiHKBudZT>B?vx zFXyVu~bPvfB(c~J<`#6U#Po0@L*!ke4O;W-?Z4jZ{XUWNKpT&zWduH z>Or-Wd(ek(*_&gpuQ+0UlnTWhDsw>)o8BcCvYzqoy>6;k^2*)YU2-dXsQNTLkLub{ zsMSLRu9Whai0uEB+f;Q=vu2B_r^JLeV|dp`zRb3nxj3LnqmZOp`T^UI5q-EP-^KuU z!oBT7u(kiF$@M~EDSr$hs7wG_b)77Zob~)HIY-WraGutvM&2!Uh4mzHuZ+4q1?n)>gR#@pB4r`gXK;>?GXR0IlZkB zC{6b7M!70nW%e9X+&5;f7n@%VWt3{DI^@_F_9d11yLk!c-{ytiRSnvBdu~V}!ZgT- zGkqwOi2KgGnbSt@7Y*qcD-LL1+zoQuzDDeGAWX3Ao&IbYHF@u_%QStH>!@qD~1MD-_985{C-xA+~BZX^}Wh#xy0rF;DOyD8VlrH2P*K-&jEuWZqBbLZ(# z!gD??4QAZZyCS}Xdxq**iZWKDS8_L~$PkBffxd~5Z>+U-g2iKhdSko-ySIYJ_-y22 z83XcIt-Qv@hV~iZkM<8Pne&qPw|}@Xb6PLF-Xlw;$sX%~q|duSpw{umE>n$*A4@cf zhB46X4(d1iZQotr`Ccv&LoAPMQr~y1yZch79Dk%=N?Bz9yKC+l>bkYVB==uA7A_uW z+E~P1-s^bn)~0mRn?9Z0lBnxOu^eIWkJ$XaDyGS=KL7Ux@m8qF2!mz^!eb!O95_8vlkw4wtSlM=TTvq3S_UtGa#XAQH`P*K+feGtZ$-FAEH}=g^>M}y` zA*cog1wE|tbU|2jC?hTVKav1nRWfD=%n@*SmqAZ(o{F>OMqXvzn;V*anZ{&fdj&># zf;m`1=D~q1LUleNx(J5w=1S3v7Y$)kZChE({RhA4DBQh!b|6QS9a`8|WrGm4&hFZT z8YpAzO1nX};XV^7jNNSK`3wOYSnBt{Pl1p)@82)b^6e}D6;_YtxiSB4tbW3>!}x(00- z=w};3@FcC&CnM9OW+2fMqub<8ZFqJtr9!?Ne0{QvmpF_XjEJlRL#|hQO2xoHatm;U zUAYQSC_pL`N*`}iAc9ac02P9E<3}G4p)CfSR^0SeG@4`O5B|*@-M0gEw%&Knj|Q#f^I`8uXm^EkwMuGccKcVIZDK1(=?2#=B;e7anUf1 znDaGg1rq{i`wniYa;r;xZ$$#6o4px+b$%y!`gFew11um=C#H%PbfyuV9=K`A^<@jz z5Uhi?aW=ugcM9|iS#ap19F5N5dxTMoT0p$jx=r{r=hd9FbtDOR0N2)E*zl?E{xK$sl2c{Vdg?d{gllMQjD`-0{e|+os;VD>6@SgC9SdC4 z!)kAjC?;uJc}p}JcX{;OO{= zXe0qvo&vvl547j#|1HGFGqxK-nOe{1Rt6a=1d|m2krt4@#E5ztWuzqvp2jk0buGLL*Hex-b_Je#vM=l5^=>hSgKg&;l?dS;)SGSwkU1@DPOL`6j# z1$jYz$gx@tUAwm<1)(7lhMKvxEipa$N7{dKms?f8^d#P-F)*N!7>XgTaD9xWaFB@~ zJHHB;ngtvNRWXKi$c8H1cu>%p&;P;+lK=c?m$ug~sTDCZ6(y9MLpK#n4_g#O(q$iF z!%9Vz=5Z=_HNa|UFO#;Iq3SZ}*Sp8AFx`MiQiS&2-9lmOhT#S0ci-*Z=r1t)iN_;|E)t2&+V#(h_CTy_^P+@Mai6TjA>{aFr2w#J<%Oh7x;}^ zU5@kh{2*#|q9y|(03UG2>0Ya%u$%ITYJF>K283BhyCZfm5YYF-Mn@x8R}tvezH&K` zpe~h6Oa@T9ZjSD1CU%LTrk&>N-Mp`jcT4Bx>XYGcUdHzxfcE}ysRJ{+YU(76OU!m8 z#ep~g>R+ybiVZyC-DWr3elgb_*Z^BCd8GZB?>&Rt(z9|ULl?UpkbM? ztGM^*GuEp3@67%pCtaG)mz~%s?pMSQd@fU*s1+{(_>3WDn+i)ao711$ z*UQ7P|0M4hW_?89?ai61EXN<_Oo!q@g!OQ>Xm4avkAJXVlPvpKZZ&(D7f=3gKH0gIKX!PRNNipITI7MbDf-)e6u&&Qf`%W}tC$#U}LD98QHcxd2I5bQ+Pc?GC zF@ls7vbq6OrrJjgq3#<`((M+umvSh%tzC@J(_c7A{6WuS3US>T+FojEYM4!M=nVi` z+FD5=cQ1=N1-0M&8|P7E@&Yp+7{B?aD+=* zG{xo1_dr8|03#$NK~oYsi{q{}AUr@stZ>#Hit&F=ah#mtuPY)JV< zF+BRPR-ckoP;h3rNJlR*q?AYr&k)ox+Li8nFqgmv+8{(UgGYEFA7Bw9xZfit3m_4} zsPkQxG?4x*q;BwptK8hH9}ihrSk|#K(N0HL2gsNT(8C0gFDp<*do#a)a&p_}kGLFI zC9fpG(_9eNMEs56=kmJYix*$HRpmw%%aaJ#eWC40507CE*ZB2KXD?_3+F3~ms;L%Hd%`uIz2a)LGcsC!8~&Ne^=BhZ1sfwu^%XZ)Nb@ceUZ9j=`Apsdj3NqoRf|k%)NaxFsV@TzzSnU5O1rm7t`d6YDCEH+NZV@sH5VkYW za@<}WOKE73yAwgIyK>JoS5$Vr{uTm<=8ovz3@g14ZD%Z7W`ac{^ zn!OJ4Drjb|ePSbXV9wDrIoPTH;U_I*Nd0D^S?(zLm&pbp@Ddu-*U;03i#Pod%25p_ zL0sB1KqRb@r=RwBhQ?%}KbsWPBVY9@;z1)j?wSEB?}S}9T6V2)8ipad))e72lKPC0 z78M3jT$?|&Gtnl%3DmU6sIhHikc-olmNMSZ02b`-fw3j3BZip}{H4-%A1N^$2z^+t zUcKrKSg|AdW;myIp*_;f1{oDtPTGj`-(R6{0r8AIAb(*XE*Kuy0l*C%;AFDM+5%1K*_zX1 z9?+mjK>rkFJ@O+Gm{4BOFoL)By;nqmLQA@F6Zq=>4@%T17!3&xZa&3~fl6Z&bpD%d zH}dX-1QrRKP?pdLKJBSWdmU0LRl!hwOm(;vur9(7NTMZgLNLU(KBPoi;6quS6&&*3 zD!v=96!er*2Mf#>=tWQ>x%EpUC3ylDKU$QBJZd00J`(t7XJD;Uf@}ZMn0OM;m`Kk>q8$2n|(9FAg z=k1zZc!mE@KD&vj`e5irT8H7#)~c*QxgBdhJu{URogeK<7d;e?@|I^GZ>7JtN47sS zOf4wtA7UyPPf!cm!Oc;a5Qh1hld+4IMZ>lpnirrS*hZsOPU&^3xh~>JLuyDD-IEde z`qYj@C?aUmFejXntV|pZ27dV=z#-V1dk_iLeS(3MCj5Kkp`6_{6wld&7lJ0Wxtsb>?h8Fvkq`z$246X4xv2RHECc1lLUuj}L_M}7{ zT_IZevnVD_=0FnPg@*<0x+7TN4W)}@`+n{5F+EB?!l6~iIy6}oyUpIB}9(u zUlRu!LqkUW;#KjSRvJ1qhpmMo_-KtUh6KfgmqOAl?JFbnb*d&XJU=!(utFh)Z6K7E z4!437dHY{-WfXVqXiK0bbECc_Z#^S)X8AOfMZ#mu3-?WS(7Lw8&Q4f}RgoYH1Q|of z*M!Y|50XmRY|e(9#QC)}-Pp%${@J|q=#!y~84ytV$%M5a{OSFwEdc#oZNL>=B|e+2 z?ERmvlR2;_WOvqWeXu>lVqhd~w!a+O=zZklFj~xhS8nuvvH{-0)YMNfW@rlUk>uf2 z-Wx=PwfI<+IOR5{%q9y_`G5F9l}el@-#v(Tvre-ia^Y~~1y!w;-QMn+P7TyNynK8| z`llY}mvxD2*>UXdy9_^l@@u2Gl$R`~*kZrRUtW%}7<;UMJuG17u=kZt*qEu`>|dzf z6-BZzAQ&qxF|y*2mH=1J-%oNMeO0aAI@}NN?pXejE>i&oX+ssl;9BtqRaHr`S~Rnr zVa>1z#xT<#&0FXnyc0|RyK{+5IGfuL)M%{<6xa{D?T;oz@%_z2^)rv=IV5!N35bV> z_(Ro=M?idqoBM)4h|l6)zkdCjH6(;cykd?1a8u~xK^MnR0hz{@-sR(yOM!4zxzs)h zlrO+rC?9YU*blfZg9o*J z3|bzsy{8~SZ=yl;=9|+FYkcj8-DPcSAv$3%50x`q=+9(6Kl8u-ekw*!bD=hUvWfE8;{p!8-4PQM z^ibhlMCFA=MzZqqz5+ol@T)%cHlU*JEBzv#RdWmd`_FfdDCrFMnE3)H9PD=)>FXz0 zDIvQ^JBpZ;{=JgGEPp&)+_R*l*`Nt__gG}IB2%n-&ypy%z#Hw;6E(kl701rk&x!oa z9k-%#K-k1T`(_`*#0+S)_F=LO=Dbj-xwbc1Fp25@>>W2**>h$EL&xyOO4@x~?a=v3 zJ6=ZVjx4S4=C9l;axC@vf+$9qe4*lcf&g)KkS3!b7jtOkUjdrw1Jr;T@$Bfu5I$a; zd;ESiM^tO9Q3R9Lt$$|EX9RHhNrmT7khN-YR#DuAx)Yf~hq0zM_c7GFsR1te-SOh~q-OIA2(N70MY zfO!48)!+}nsfs*xb(3+qHS3Lm+1J_}$g7SwQia~sQj7iDryw&+)b`HK9L&KFaz(=& zFDQTjH?mZPLp!c4Cz**0VmO{kk z(3qxxItpn=gT@i43&ZJ#P^5gK3W*MWjyDx}kd2);WzaW1-(((8GSTGJi`$Ys@RuA| zuNNQmYJq?;14$Ji*&$I9W#C;oKvML^EHnh)bu9>!5v=R=J3rv`Rd@}rc9GGcEtSN^ zOW;LAN?$wk;Wu}>Z* zSN5EjX`EMyD})+&Q@D$nI|`;7si*qYeojpM!G)W7{Q+W4h!;P>M>V$y4A!dN*LCPi ze-v*-G^zKAsB+53b$0Y73TY+v{TeRn$s&Y)dpB<|Eu&tmX*A5J((6@6OBlO^qS4&l ztWQv~va+9Hf)|-Ea9bTky6P=0EsxfuA;*Cy+59Q?u$K=z8%`w?cs}3Mh@8p2vW@V|TsP3pYZt!h;8}F%vpSjR7x+zTi9Wq}gk^?K zo@FE&z1q-+!q$^YBSZ-3wvcIDmfxOqi zw#)$DSO6oUrZ8{}($nSiE_pfIlWX5E=k<8y(O+C3<>)qR%#Yxo79X#8;`I=6#{MvA`4RkW5mG)=_%#^PDO^zVz#qEkNBPHd_)6-QpM7ED0 zS`a~!ME3&jg;AnfK)er+%l{v}35EgAY4V8YH?2wqw;A|t!ckXfNxN=7RZ9;IV2C>b zf=J-tQ_$08_?9)NL;ALCeAegqiXX?T3No1yT9;0oKr##|b?3#g^-<@DdG56q}bT z73HeJU>c9pbL+~3>k)LA#5vG*@Do6Wf|)JU&1b)mbb;&>G*28L(%fh#j|r!qs@ltZ z`oMkPOaa>-7u=pTo@KUAQ9L4=N)v7Ey-QnpS`GcjyLU`1i6*}%Fi~B96w;X@+z+EE zz#BLa<&Zm=oF3sZ`4LmF*X?gbnFXHznJJU{^w%46n`06CTE6J5y$RW^`QPz3&}g^j zK9q=YLkOjMHIOr*phkrn`X_7-*H`6+P8z%nZ(P}+Th?Dqv_C)Ob?0?nZc!7jvV2V4 zl^%GsZ`4ZuNq4v}OkQr5@qM@lO5l1VnmoaN?)>)hVyOqutq=F2u3&$uPcFhJZBId# zNm1JyqYdNI#z^W`T#euUfSe0^?DllS(GQXQN(d{4@rw3U>C3YEG`+z24rkf7Ht<6F^kp#P3!9NlPnVoot{zosQIVE`Mv8{B9-julK9xlkbj#D{r_X z`PLh$qc~1MqAPg5D|M0PEIp;E%M_d}6HXQo5CAV(pg}=cL(&>Zp6km_0HlFbT?ctw z?4jM$Kfn0gh(waHRGj{`x4P0@bVmlO#2XrAsyw5XPW0Vnn0@5sMgv)K@_~`A+-Rb+ z9?E|X{6ZXuIvZs78QmIq84`qeqzOhlu{w7D+vTr%OEi-k^`yMdDBa!jP0v00gbJs6 zULst1$o*A2i`#&V%*~`#kQbTBgU)#t%pQOg4rCGrQ!_I&x~CAi}TxevVra>TwI(^C;9?}W#nFU-hsXdd$D*r5)Og?+Iw!dGXaT8%Z z{QdnY-||ZKDNo1Rvar{$Pepy!Loxb|I!ydn9v&_q=&ka?=9>)oN>M9>B1)R}ib`J2P*${E@e-pfpH#3M?3p3)yCb%RK_ zzG;A8;*6b>V__DDIPq>~Qbr+eoZ8@4a}>SA*>35z#1m4`@=nI7UC|Lz%DDuTd|j-} z6|^H0r#^AC)Zepy_(XM0k%<^FeoOL4xyxt$w2SK8CVrMoAi%uk6qG>_wMxWucj%<( zBOE$rCEU2PSIQRu;KuW~S^v&|o7G-&3&*M*PQ0xEsw!GVU3!VbuO|A^CRbR_Xymb3 ztJ}6ExV@A%Fu<SJpsZG{udLz0p^{PScjGvJSuL}tj{Xh_w1JwXD%@2)m(u2!GC&MTY0@& z^y1jT&i$Nt=A%phr?fMVrh0Gx{?nJSqw&r^nwapQS^oO?a%et!4w_x$lZYn`*sTIFnefA{x$eXimCeq9oW zK4fiYJANY4;}3spn!~BUK%`%5P)%-dfjs7RwD&|*d%589<%i>#vo$21l(5ok>6PhO zPG`d9Iqjqp5+S5qiE1}qqbNVq5rLF)P&cE!>_hXg&uZUau25k+8q<=ZUu7PGg6JmR zJu4Pq7)B~jVHaX5zC!9t=CRi5R2}_AE~|sp~^xUwF@E9S>)6 zifQmOQ$Cbua+>S{$YqTxMWr9I}|D zrSrl2%F2b;S%a%#^q$9p<;LamsNV-PPs5boD17C z)hQ-4I{|bZVUfLu(f_UR@B>ImU`WdD-Tgni&5wx>cD0Q2WU$a4|48iJ(EeU=>85YP zN$J{?pZvv+zeqg2X4z;nN zX41CR$Nbb_NDjUq3_dl8Q$I_`%S#Ez&q6XgVUV1ekvD%0WoB5fid0zA=RIQImVUW) zp+L%Z-wI>XXxt-`4>FmDPvG(q9}kX-B##(a_mQgG0xyEuY<92fzw3O3HU_h z%9r(Cr*&3st;N9?m41(vi4+BLG4VB&nX%piNKHwPLUmjBH|qTC1TTtpbk*ab2cb2% z65}mcQbK43WC>XXvq~}-g$1|{u+7e``yw+J%>8-z11mM{B-8QNCrqDU>UZ9miO-Tc z)%gJ4c@fBa2-x9V9yA>f+?w12Q1{(J-qz^q`LEviTN6h;Xym?#4*x0cj@Zi)eJa}W zYel6;s4KSafleo?g3<*?sOg%rItp=E8vy2Rv9~@@H zWk?jfXcax0!C|lp%C55O2d=q5IrM8;cfp4`oy{vo6pKn&>$_tBa)6`_TD8ks&P=+P zKcZA4oF6n$qr!&r=9}ncVDMZI3ers11*r_vQRg7pH|P=0;?7$BgI9!MQ=ll1zn`;buO)jk+|G2?pa+T0D zQ*Ns$`Mx(4z_)W?Tb+dIDW^uYH$Q)KQhZN)2DTh+Zkut~7%aLPu|pcD!Gl+?2zX8^ zo4E+im`%>mi%L3XX3yj-yuxDg9@b<>X<2Sk{d0~hsHx=a<>RA$QqS$DmKksIO-2Wa zlP=n~Xe~WLWxiv?H|yrvf^C%ho^Lmdx>eeYIok!ZlAn;nZ=v5>%6ed%A?N(c3;9$^DqJQP@3q;IXsshpz1jR(*Yeow zw90HTlUVX4rD`x8Zm9o!=j+SA*?2E+-+J9$Y2Q9qeNUUc)}<5x?^sbh(&u3k8SHOS zLp2E`S4nh4Zxln)%+vYQ_tSih*~X^itMiAjaY3GDEgA%E>+Ck zxi{ekHbXwEm0FSDT?*ESkt)++lOqOPNTQK}+YK@u ze$M4J`GdLdYU&aRiJbi_X&E@zID_HnF!_>h`8D}LoR%Ad3PyzqZ&&H1*%`Jnxs@)q zwJDKtD)4+1u0#XLrY^FTHUG2DD6dH}doNnuHEJA1Fb*v;Z*!J#d~okpzcgADC#U#&PXi-y_6Xj<0gnXAmfn*Wr5MBhB9pVz$(9jm63zoF7PBN;h-nz{ zKz7B%G(6qEAh^6Wc8KZl_qTY)t5Z&mS{IL&MnLb)!iHI&zN25qX`Z>f-jmZ40gn`f z44%P5!+UMf<9K7FL}KswFE@vq3Qp$DyFWc#bJF{d+C8u?Z^v$- zXl9L#R(yT6@sTns+&@8U4kwlh2yWQA9MUibqc1sh6#}2|!(}JF$0TUQ`gr3DIPy`F zIuTkRlrPsT|B%C}!_Q}S&@F=eW^iNUMa{fgGrNXhg7)(1uWsyo+<0XPkHYugqmR{Y zZY=R$xTqrfl)KLFc{J=VObmuP@|k7G$w%Ul6J(PRuq0Vb&tPl;Sp|$57HI+VJepcSYV`4 z_GoBVQ<>+Vn_RqtE0co-&$FZQ3O|d8%B0wRtdpJG8X}Do&5HZyn(k!EFI(xtP_pHj zoN9Dhcs;;w#W;DF>ngQoYD*Z37k_H*s~Re}q%zBe+N?pa<0JP_0Tb?alQ1ol#XSFm zAOJP!f3Xz#pR=mm^z>*6=bYwUzwO3@`jf%eFS#}2cUu%Ho2cVrxBY~iIMtXI3I%3{ zUE>$XiVkRN$3y=FpH8E53@Pwq_p+drB7i`mG)B|&6fE0kjp#GWS^bM{w8r?heA5u1 zQAJNOb_;m5siP#?a&*Nm*Bo6IAZHzw7eph0GXoI)c9cg&qUEUvt2durB~VdqZEaB6 zTz5JSnd(y$x)eBohpPIKVyxogQg?Qifn{nU!PRj5^X9L#G|Mj!j->#pGb67$!Eo20 z**nX4O$ac$RVI4#@k!48qfq^qAlSG;#uY<)8K`nTzJh216YBdcCn6PJ)eAUD<}qH0 zFy4**RT|?PbLI@MN)zYYmj?l7V%~+Zpc-;i7MELAXh#Em#P?iHst&ks_F0XXXDw%E zKa?&8P$Mk<|0oOE+S{WlEG;Ys-NUX<)JYn+u8R6(+W_<;4BGNW2`7bsiJI8i1A73pXFyQqFd0+02 zfgqoGfxaJ(SMtP;o7cT!;hKB$grA%Ha{Hjr#M`ra2xvR$>J*uxuSvQ>NT-alUAJTO z_ikYK&hyo$K}XqoAIj<=v`d6W+X1>~V(-Ct0Q7*5?gD1;0fB8vA!BxEpjJ=kWvh?%*Mz}r zE(sPT2zu{PJJ#iTE5eJM{!7UOm*mZbex5MB3^6e?_N%NakGU1yzdP2;9qy08IV9ll z4pQdC!KHmCZ)AvMh+#HL@u6h_GW=e z^@CZ|E*SL}v-!60=-s|a`O^;~4ae|Rd!D8euIb3d2(9oK#puPN|N>oHz zo=-llRoR0}^TD(ClUi!)D^Tic$`y1!mLHx>;U#dzMHGaVOD3S0}da%&<(|eV|`!5#d&Glt|)JQoI zdVUl!ZpE6G>zmF0oYOG-JqtQbXnnBK)CQ%IPqPZB4Fmab;3#H{bbjPIdo)`1w-fxN zJ#!+mI?ZrX@qxlA@Yyc&HXl?xYjxUvcP;cQ2FvW`) zXfYJ8rF3R}@3^!1a{;eU#gY4ke~gz;4{skHKSXydd$_}1kbhT4=5mKb?a<+Bx-{4J z6`-)#t+|*)V*gaB^x!>+Es{LWSJrv!OU?Ao-Bh{=v{?ZZtN0QQP-TS$7d)|>V2coi7 z5^=2dw)L>*dR%zi$nMm==f=8F-gs^?zmAT(GZ&hEWu^osbMRYq#^U0rp2uh97}@APSeFy4wI2}mrb)@x_L2$&4y0RSdT2q&h}RCiGNXZV>EMU zj#24&1D3ma71a$I>^6<-av9$_S8)h<`b{wPJtCi#;DSC|7(_-sk$L~X~0IJa33aZl?+mYB+rPG{d_ic^Bl0x*(fKt;!dc) zqDlCB7wflc%8!5eSetX4+!BRRPnUib;8$J&C~p9}<7XAc8{!gHqC1}&b9a^bo6ui~ z!&IgI{q_lpX=8cR;pDaay9$Mxu0Hc{Uv2zMts?s6xqE1;2L>u0p*UMXE7e!!FMVe6 zUXX!qLn-~2)nuP&v1zrJ*FP`w;xVP?dF~9m=j*Oo@sk@KP5-b`zG3#M`_U*ETF6AblIMKc{sr|SvxMJ zm!@l8?7$&q^(s>yDeyr#E{bVkZq#x{Q@p6o{PbEsUDnWqrnZ`AMxU)6Aq)||-Sm`I z-ap%mcYm(yEfqn#eU*8+8<*=IQ&I7cE@mdJ;)RxzwfXsKRsV};Y$S7;bV@Z-sn%^D zNXa-MZK__vd@-bC9FgvT}cPo3UQF^KKa&rj)J^<-fE^hu>-P=&a{PKBYd+ z(&HFjhNVQt!d6A*VYk@!Fztxa(!TQb@D?+{V`ZF#jy~)zXrTT2nMX8Jp=jo|rL33N# zbG;$E;>^R`FIoZ2yJT^1=1%RG8$||mEBlFFe(zzL-46Svc^{wM(YozfEqt=|=`FJp ztyP98qUhnMODVmjB<>Z_*h9a#DtE^Kp~qB6Ym1YvagIql08%q$(YRro zI)2xI%YGD?K+ujQ{TB7YrCTV88Y~=b-`;HHS7E0+6^wKqe*3)YD87xrV223t7HCum zNeB&?fHG@KAk1iu-B7_MfVE5pFhDYW2bXOQWFr={Uju9cXsRLxc9oG*O!%T1U7HdT z92|$A_L}ZZdiK)?dSh~c!zi8(Tq}h4C1?|L?x%2#fWCBOt%|hTvoL=ykFMSM!NY2=Itq?xBLA86~9%^bkytAr~;%x^8UyN9r zuXq&9KEjzn0=J{cp{taasJ4LVw9nE-4+)dPt%CRk!n`-?ne)+W8U_5AKuG_=k8`N0 zsnLGzL5{U?%a%`5TZ~?suR8|Lhww$;%nOtTxCD?fSWXVN5>GuOc@CI`K|SIAf10pl zUZChx@0fFlI8PZeu(82ksF&;4%bKUNg|%-;Y77{fP&J?~f~Y zfwculo#EjF?$bbBP|&N}0w}-w1|2xfLvGx#0%Gjf&$q08@S0$hEsmM|a``NKMVG#g zhFmlX!QUo!1jn%+JvjQ+AyFYT7A$;-Spz8<2~j4AtW;>Yp$g2O^1_6e;yl<8!7FVI z=6WZaKLxb+R!l6sAl@Q!G$eHQz}QX4yMln&r}%`UX7EiTb&@xgk7~1P7R`<-Xq8hc_lUCazqf-RY0hkEze^zOKh+ z0bdc4-(+vdz`QPMKb3i#BdET`bO*U0O_pLmf|k z8LWn7;5?PVG@*@WQ3qr_ZCl$HPLUW4KEw3F)u)r4*ucvzLGri!mI zvn2{(#E^CRuTCEv{h_a$AkS`|s2gL4A^xKuB8I|vyQZ{*GbL)7P^D_nN@Z;+tAu=!yNj5b#S)gMjI%Kwgj*-yCZ`vwNO(D#7tSKopcwzJn)k_6kiCmv;OY>!j%x-Zw z%3NN{5V}L_WrAgbu-e7W3F?H!;eaghScV+Wu#Es>YzGX6u6EL3gskS_u|YL?L$W9b zeDki@071>)CkQnjdVlY)Nq0ZwRSfM%sIepjC_;YXHAkstm!<2Wpkis~6^a?B)^Cow zJo-;h0JW_u5u&(Cq`rIFgh)bkB5mgN`)Y|db6S>%kKFBVD*i?BN-Boa4AcH&L#Ou7 zvYiCQ8J4{7FF>^XIMek!rXWNW8(TWg(X_^Y)iDuQEfFW#HPq5|aL;Zrc)ALhK_+ul z*alAzU&5|~Zdtt1jbI!js4LLu<2;5mx<|;Hk)hG-I$4zQRQH6Or7oxiLC{A05+wG$ zLKEqoR(SuJQwpeVXv7kQTSS^VY!2LKhe@~QP%G3)wQtXpB@x+nru(TJK;=a!d&)XG zIzq8l*}2<)es><<=T9;3Bco!Hz2;iR@p0zZu3fqE75(@AK8Lpzr}nqG%;2E9~YIrqje+uo$3y}Q7N4sG^TgA;?>^bzVn-L)TF0)SI^4fTv6PeDw>ip_-$3um57CkXaJO+TZ9-XCPub<8pkWtgL+K zCjeak`>l2@yj2LqzCq*;L&?>cn3$^0Pdk4sW%&I|0R_tbp-YRFJ}Lna1z6PyCw`u%+PV4|n&wb%5kMmPAh=GDaZGFvn}B>^1hSsh zLP8uyzCI2!PC3<(H?GKsDxIl@%E5%pp%_OoMkkdQ!X7j=?FAlC^X``S5Y1`hREMj= z0;)&xQQn-oDbss+N^t3XYuI`FM_HaJ7KcppHsCoRi+wQ3p}M=bc6NS5V~j|1het+) zK}6rUJAfCvX%<_A#G*%YN>N#Z|>32LJ*1CDJ8>*&%x*4FIQ?xsTc> zmvbC(3!TqE>OjF4$9kGEG5j2jIzl77ja#!`P)_+>fSknwDyCu>PYT_`=jLw}f4PeT zQ?thVrx}f3Zj?*L`7rbCJXeS}LvFZHJ!tB|!R=wxtx*Z!z3kGiYhp~ub%zh%L`r!; zQf3}D+42j>mCi!je!4o<^gQsacKAGJ@o%q*uS<_7#+#)$AWGr$)dptKPJYb0CI3nDDMpd?>j)qc)>%r8BfxieuZVeX9A&nDo`m_dF9<7qQxiP{A#)0) zPr*7{)p9KiZ9v2c>=41S6dHKmk>uPuTc43a#$}F=u+bnAb2wgD*Y9Ap(r90DT4MNz z_dJ$>gX%!iOhB@b7#juEyzFD(&+N0xFvkRc751J60}Q+I;D(e~VLJrOko%&C3tzM8s zHBNe&uIhjLoTe_>d2vOje^s>HU?@_o#}F>9q`Mxbw~2g&<;3sd)K-%`>MS0}x_yISiW!;_G1n6k$ zgfb!*0XBe!un7psiS%tycIYr2F1&#w49M`2RqNJyR9?WApp^csy_>beRmt46BHOul z4>+GZ($;M6-@hk`GLf#{y<3P|7LTbS;co$dP$Mu;YxaplC4^~V>z6Tbw2)V>J z4$&b1RHJYt597d4sHp*bdAIlUS2UB!>L6rK_EbU0GV#)Sk5!qF>ODi_8)$8ZR9c-a zsYCEL*v&*DwH@^lQ11kRfaDQjmh2>`x!?reDfu%ZLcsBZV9}0eWNQX!va3<%ZAROa zELA|$dzU{I9}G)>_5=+e6g+yeDYC2xx#S_7+vMaz5{7@a6CohcMdC^$vV8>3@(BMK z^lJ@LPOO0_9M5kNa;AftnwpLc61kI?fwR30@eELG#A(Mj?U-B$_z7DX4Un zwi!J216!TYvoW70(}ZESEyBHFz{BtXOL!UvJukQ+2upXu`32II5xgGm772> z^D3C$bm-A1;H=y+@~^|ujJ%~2Tn1<6*Pvhvdi4B_fz?suiB#;D!xw<9F0bqwcM*&! z&SeC@7T5v~rt83`UW*E9XN+>7y6OP`fwCSs20jHo;Q<2!E`-3x5JwXjDkQ!rjsxtr zOWeN1!|BM16kHa*E7>9*eLR@R77el5XaUyjy8;*g6mo;R7K9W16@&A$_><0w}&M_Dw^>JqtLp1&ZBDD-GPdFG`F?`RnJ8tp7O(*(CGR|(;pdQ1!sc%1}AK^o%FNWpd#u1M305fO{& zzaUMJMfsBmlr^-*?1D-*l-KxHyJZ1rc>sW^+h7f5RY124MCKmC{aZ#L{1$b2=NFga z0g0-h+16Q5q%ZuG>3#*IeL0?9g&4VWKR&@(yy>H39=4r98r&2qC?_jhO|iz<2fqa| zdZl@JjLJ}hH9jKq4y^IY>s5fOFcCGO92qa=PWOqzUFT3!Oznvc7#^OS_uT`oHrDA~ z9;IVl9G-v{)Ms1AlaXtVo{^A{z>ux$MLVLyMr`Wx^P_af;bx$0&5{)YY}mMZ?dsJ9 z(D@!js;V!RW|}=N5H*Ttl5Cp$rXl1aUV>1BG{$we3VbpI7m)!1EerXR-0G^e`JP8% z73{7+1fy*A>BfIZ4T=mf|Mv|4&T|Gq&1cZ1!D-co6I@Qo4mAb#YUB)hXCF(@yhl9v zYqlA*5DqoIW^otOXMu{OJm0p*DkhJnJ*^D%uJr!jpK=+QbGW6{RP8$%N+Yt4e zx{0Z%0S;7($Bq<)?yzgtVJ!j~P4)T+-}ZUB^l+ih4FSdb zNKye4yw6qQgv~QJp!JvBo>6dXMp%wgLEVW1mi^29?X0R;xl~897Xr>WR{i>4TLsjHPgKio9_ZjQ&qh6WR5&4K=?Gv=)W+d;#Z=6oyx)PN-T0rsO`dixJgRt z`_u_^jB+|)!+$=;VKC6qtt;v{61M?OO#1twF?thm+^9+PU*01+ND^Jp)MHQ)B363%_jfuce0%gs4gPKpSXKz_#O zA>RcHJ3m>H!X~gwrm7fgF|qdS2BgXk*mMbSHTk+Ni$YGJyY&eBKk;jw@E2!pa^D&Q zm9FpxrpA3)skvwkju4VkZ&C~auwYYAQe^1OcYOFRtZ<+jJU6%wrQR#F4{!o6Z?S!= z1qcoDX;Qj^^>ha(i2=>Xv?6` zgDi(SkzsofuWo~hI&R;*xLx+~c(%w`7yl!Hdy%c2h+hW~$6yq~O(>1f-$ZR+iP(aB zoC7uW3$wgv)HH;4e~n$>3qdqizo=8L5feUJU_IyEN}9icnKBFOcK&lyC)AS2r~dyo d=$6hiQKP>VX>^W;VW%+V;J!oZse8;X|1UMm)u{jg literal 0 HcmV?d00001 diff --git a/docs/sort_facet_supporting_docs/queryTimevsNumDocs.png b/docs/sort_facet_supporting_docs/queryTimevsNumDocs.png new file mode 100644 index 0000000000000000000000000000000000000000..151b422aa7faad3213a54b55007225a564b6fccd GIT binary patch literal 32162 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 a;wYfGS1L literal 0 HcmV?d00001 From 6dee5e9b5df88007438b9ff565f8bdea8fc3a404 Mon Sep 17 00:00:00 2001 From: Aditi Ahuja <48997495+metonymic-smokey@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:51:09 +0530 Subject: [PATCH 2/5] Added missing nil check (#1905) In case a document is not found, a nil check is required since `doc.StoredFieldsBytes()` then results in a panic. --- index_impl.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index_impl.go b/index_impl.go index fe3a62e9e..5c9538822 100644 --- a/index_impl.go +++ b/index_impl.go @@ -496,7 +496,6 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr ctx = context.WithValue(ctx, search.GeoBufferPoolCallbackKey, search.GeoBufferPoolCallbackFunc(getBufferPool)) - // Using a disjunction query to get union of results from KNN query // and the original query searchQuery := disjunctQueryWithKNN(req) @@ -663,9 +662,9 @@ func LoadAndHighlightFields(hit *search.DocumentMatch, req *SearchRequest, var totalStoredFieldsBytes uint64 if len(req.Fields) > 0 || highlighter != nil { doc, err := r.Document(hit.ID) - totalStoredFieldsBytes = doc.StoredFieldsBytes() if err == nil && doc != nil { if len(req.Fields) > 0 { + totalStoredFieldsBytes = doc.StoredFieldsBytes() fieldsToLoad := deDuplicate(req.Fields) for _, f := range fieldsToLoad { doc.VisitFields(func(docF index.Field) { From c8e3daf115aee56897e841aee9ddc9dfa78ba5d6 Mon Sep 17 00:00:00 2001 From: Likith B <62029862+Likith101@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:04:32 +0530 Subject: [PATCH 3/5] #1873: Added timeout option in the Search Handler (#1898) - Added timeout value, which the user can pass through request parameters - Create context with the timeout value if present - Change the call to SearchInContext --- http/search.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/http/search.go b/http/search.go index 186d3d2c6..37a33f031 100644 --- a/http/search.go +++ b/http/search.go @@ -15,10 +15,12 @@ package http import ( + "context" "encoding/json" "fmt" "io" "net/http" + "time" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/search/query" @@ -80,8 +82,22 @@ func (h *SearchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } + // check for timeout and create context + var ctx context.Context + timeoutStr := req.FormValue("timeout") + if timeoutStr == "" { + ctx = context.Background() + } else { + timeout, err := time.ParseDuration(timeoutStr) + if err != nil { + showError(w, req, fmt.Sprintf("error parsing timeout value: %v", err), 400) + return + } + ctx, _ = context.WithTimeout(context.Background(), timeout) + } + // execute the query - searchResponse, err := index.Search(&searchRequest) + searchResponse, err := index.SearchInContext(ctx, &searchRequest) if err != nil { showError(w, req, fmt.Sprintf("error executing query: %v", err), 500) return From e26eacec92d4fda5d953a8581b4b71b55b0578d6 Mon Sep 17 00:00:00 2001 From: Mohd Shaad Khan <65341373+moshaad7@users.noreply.github.com> Date: Thu, 21 Dec 2023 18:14:59 +0530 Subject: [PATCH 4/5] MB-60207 fix facets merge (#1946) --- search/facets_builder.go | 18 ++- search/facets_builder_test.go | 276 ++++++++++++++++++++++------------ 2 files changed, 194 insertions(+), 100 deletions(-) diff --git a/search/facets_builder.go b/search/facets_builder.go index ebe785c02..4b1f2db78 100644 --- a/search/facets_builder.go +++ b/search/facets_builder.go @@ -321,17 +321,29 @@ func (fr *FacetResult) Merge(other *FacetResult) { fr.Total += other.Total fr.Missing += other.Missing fr.Other += other.Other - if fr.Terms != nil && other.Terms != nil { + if other.Terms != nil { + if fr.Terms == nil { + fr.Terms = other.Terms + return + } for _, term := range other.Terms.termFacets { fr.Terms.Add(term) } } - if fr.NumericRanges != nil && other.NumericRanges != nil { + if other.NumericRanges != nil { + if fr.NumericRanges == nil { + fr.NumericRanges = other.NumericRanges + return + } for _, nr := range other.NumericRanges { fr.NumericRanges = fr.NumericRanges.Add(nr) } } - if fr.DateRanges != nil && other.DateRanges != nil { + if other.DateRanges != nil { + if fr.DateRanges == nil { + fr.DateRanges = other.DateRanges + return + } for _, dr := range other.DateRanges { fr.DateRanges = fr.DateRanges.Add(dr) } diff --git a/search/facets_builder_test.go b/search/facets_builder_test.go index b54d87fa5..6815b1ab9 100644 --- a/search/facets_builder_test.go +++ b/search/facets_builder_test.go @@ -15,122 +15,204 @@ package search import ( + "fmt" "reflect" "testing" ) func TestTermFacetResultsMerge(t *testing.T) { + type testCase struct { + // Input + frs1 FacetResults // first facet results + frs2 FacetResults // second facet results (to be merged into first) + fixups map[string]int // {facetName:size} (to be applied after merge) - fr1TypeTerms := &TermFacets{} - fr1TypeTerms.Add( - &TermFacet{ - Term: "blog", - Count: 25, - }, - &TermFacet{ - Term: "comment", - Count: 24, - }, - &TermFacet{ - Term: "feedback", - Count: 1, - }, - ) - fr1 := &FacetResult{ - Field: "type", - Total: 100, - Missing: 25, - Other: 25, - Terms: fr1TypeTerms, + // Expected output + expFrs FacetResults // facet results after merge and fixup } - fr1CategoryTerms := &TermFacets{} - fr1CategoryTerms.Add( - &TermFacet{ - Term: "clothing", - Count: 35, - }, - &TermFacet{ - Term: "electronics", - Count: 25, - }, - ) + tests := []*testCase{ + func() *testCase { + rv := &testCase{} - fr1Only := &FacetResult{ - Field: "category", - Total: 97, - Missing: 22, - Other: 15, - Terms: fr1CategoryTerms, - } - frs1 := FacetResults{ - "types": fr1, - "categories": fr1Only, - } + rv.frs1 = FacetResults{ + "types": &FacetResult{ + Field: "type", + Total: 100, + Missing: 25, + Other: 25, + Terms: func() *TermFacets { + tfs := &TermFacets{} + tfs.Add( + &TermFacet{ + Term: "blog", + Count: 25, + }, + &TermFacet{ + Term: "comment", + Count: 24, + }, + &TermFacet{ + Term: "feedback", + Count: 1, + }, + ) + return tfs + }(), + }, + "categories": &FacetResult{ + Field: "category", + Total: 97, + Missing: 22, + Other: 15, + Terms: func() *TermFacets { + tfs := &TermFacets{} + tfs.Add( + &TermFacet{ + Term: "clothing", + Count: 35, + }, + &TermFacet{ + Term: "electronics", + Count: 25, + }, + ) + return tfs + }(), + }, + } + rv.frs2 = FacetResults{ + "types": &FacetResult{ + Field: "type", + Total: 100, + Missing: 25, + Other: 25, + Terms: func() *TermFacets { + tfs := &TermFacets{} + tfs.Add( + &TermFacet{ + Term: "blog", + Count: 25, + }, + &TermFacet{ + Term: "comment", + Count: 22, + }, + &TermFacet{ + Term: "flag", + Count: 3, + }, + ) + return tfs + }(), + }, + } + rv.fixups = map[string]int{ + "types": 3, // we want top 3 terms based on count + } - fr2TypeTerms := &TermFacets{} - fr2TypeTerms.Add( - &TermFacet{ - Term: "blog", - Count: 25, - }, - &TermFacet{ - Term: "comment", - Count: 22, - }, - &TermFacet{ - Term: "flag", - Count: 3, - }, - ) + rv.expFrs = FacetResults{ + "types": &FacetResult{ + Field: "type", + Total: 200, + Missing: 50, + Other: 51, + Terms: &TermFacets{ + termFacets: []*TermFacet{ + { + Term: "blog", + Count: 50, + }, + { + Term: "comment", + Count: 46, + }, + { + Term: "flag", + Count: 3, + }, + }, + }, + }, + "categories": rv.frs1["categories"], + } - fr2 := &FacetResult{ - Field: "type", - Total: 100, - Missing: 25, - Other: 25, - Terms: fr2TypeTerms, - } - frs2 := FacetResults{ - "types": fr2, - } + return rv + }(), + func() *testCase { + rv := &testCase{} - expectedFr := &FacetResult{ - Field: "type", - Total: 200, - Missing: 50, - Other: 51, - Terms: &TermFacets{ - termFacets: []*TermFacet{ - { - Term: "blog", - Count: 50, + rv.frs1 = FacetResults{ + "facetName": &FacetResult{ + Field: "docField", + Total: 0, + Missing: 0, + Other: 0, + Terms: nil, }, - { - Term: "comment", - Count: 46, + } + rv.frs2 = FacetResults{ + "facetName": &FacetResult{ + Field: "docField", + Total: 3, + Missing: 0, + Other: 0, + Terms: &TermFacets{ + termFacets: []*TermFacet{ + { + Term: "firstTerm", + Count: 1, + }, + { + Term: "secondTerm", + Count: 2, + }, + }, + }, }, - { - Term: "flag", - Count: 3, + } + rv.fixups = map[string]int{ + "facetName": 1, + } + + rv.expFrs = FacetResults{ + "facetName": &FacetResult{ + Field: "docField", + Total: 3, + Missing: 0, + Other: 1, + Terms: &TermFacets{ + termFacets: []*TermFacet{ + { + Term: "secondTerm", + Count: 2, + }, + }, + }, }, - }, - }, - } - expectedFrs := FacetResults{ - "types": expectedFr, - "categories": fr1Only, + } + return rv + }(), } - frs1.Merge(frs2) - frs1.Fixup("types", 3) + for tcIdx, tc := range tests { + t.Run(fmt.Sprintf("T#%d", tcIdx), func(t *testing.T) { + tc.frs1.Merge(tc.frs2) + for facetName, size := range tc.fixups { + tc.frs1.Fixup(facetName, size) + } - for _, v := range frs1 { - v.Terms.termLookup = nil - } + // clear termLookup, so we can compare the facet results + for _, fr := range tc.frs1 { + if fr.Terms != nil { + fr.Terms.termLookup = nil + } + } - if !reflect.DeepEqual(frs1, expectedFrs) { - t.Errorf("expected %v, got %v", expectedFrs, frs1) + if !reflect.DeepEqual(tc.frs1, tc.expFrs) { + t.Errorf("expected %v, got %v", tc.expFrs, tc.frs1) + } + }) } } From 5f1f45a5c32a140546e247ba91e7250de247adf9 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Wed, 10 Jan 2024 17:49:16 +0100 Subject: [PATCH 5/5] Fixed spanish accents normalization (#1957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normalization of accented letters only happens if the input is larger than 5 characters, something that, for example, neither `guía` nor `fría` comply. The solution would be to always execute the accented characters normalization, by moving it to a separate file just like it is done in the german analyzer. Fixes: https://github.com/blevesearch/bleve/issues/1956 --- analysis/lang/es/analyzer_es.go | 5 + analysis/lang/es/light_stemmer_es.go | 15 --- analysis/lang/es/spanish_normalize.go | 67 ++++++++++++ analysis/lang/es/spanish_normalize_test.go | 112 +++++++++++++++++++++ 4 files changed, 184 insertions(+), 15 deletions(-) create mode 100644 analysis/lang/es/spanish_normalize.go create mode 100644 analysis/lang/es/spanish_normalize_test.go diff --git a/analysis/lang/es/analyzer_es.go b/analysis/lang/es/analyzer_es.go index e6fcd080c..eea75568d 100644 --- a/analysis/lang/es/analyzer_es.go +++ b/analysis/lang/es/analyzer_es.go @@ -34,6 +34,10 @@ func AnalyzerConstructor(config map[string]interface{}, if err != nil { return nil, err } + normalizeEsFilter, err := cache.TokenFilterNamed(NormalizeName) + if err != nil { + return nil, err + } stopEsFilter, err := cache.TokenFilterNamed(StopName) if err != nil { return nil, err @@ -47,6 +51,7 @@ func AnalyzerConstructor(config map[string]interface{}, TokenFilters: []analysis.TokenFilter{ toLowerFilter, stopEsFilter, + normalizeEsFilter, lightStemmerEsFilter, }, } diff --git a/analysis/lang/es/light_stemmer_es.go b/analysis/lang/es/light_stemmer_es.go index c1b4749ea..4be04a4bd 100644 --- a/analysis/lang/es/light_stemmer_es.go +++ b/analysis/lang/es/light_stemmer_es.go @@ -46,21 +46,6 @@ func stem(input []rune) []rune { return input } - for i, r := range input { - switch r { - case 'à', 'á', 'â', 'ä': - input[i] = 'a' - case 'ò', 'ó', 'ô', 'ö': - input[i] = 'o' - case 'è', 'é', 'ê', 'ë': - input[i] = 'e' - case 'ù', 'ú', 'û', 'ü': - input[i] = 'u' - case 'ì', 'í', 'î', 'ï': - input[i] = 'i' - } - } - switch input[l-1] { case 'o', 'a', 'e': return input[:l-1] diff --git a/analysis/lang/es/spanish_normalize.go b/analysis/lang/es/spanish_normalize.go new file mode 100644 index 000000000..a6f1964ad --- /dev/null +++ b/analysis/lang/es/spanish_normalize.go @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Couchbase, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package es + +import ( + "bytes" + + "github.com/blevesearch/bleve/v2/analysis" + "github.com/blevesearch/bleve/v2/registry" +) + +const NormalizeName = "normalize_es" + +type SpanishNormalizeFilter struct { +} + +func NewSpanishNormalizeFilter() *SpanishNormalizeFilter { + return &SpanishNormalizeFilter{} +} + +func (s *SpanishNormalizeFilter) Filter(input analysis.TokenStream) analysis.TokenStream { + for _, token := range input { + term := normalize(token.Term) + token.Term = term + } + return input +} + +func normalize(input []byte) []byte { + runes := bytes.Runes(input) + for i := 0; i < len(runes); i++ { + switch runes[i] { + case 'à', 'á', 'â', 'ä': + runes[i] = 'a' + case 'ò', 'ó', 'ô', 'ö': + runes[i] = 'o' + case 'è', 'é', 'ê', 'ë': + runes[i] = 'e' + case 'ù', 'ú', 'û', 'ü': + runes[i] = 'u' + case 'ì', 'í', 'î', 'ï': + runes[i] = 'i' + } + } + + return analysis.BuildTermFromRunes(runes) +} + +func NormalizerFilterConstructor(config map[string]interface{}, cache *registry.Cache) (analysis.TokenFilter, error) { + return NewSpanishNormalizeFilter(), nil +} + +func init() { + registry.RegisterTokenFilter(NormalizeName, NormalizerFilterConstructor) +} diff --git a/analysis/lang/es/spanish_normalize_test.go b/analysis/lang/es/spanish_normalize_test.go new file mode 100644 index 000000000..b2f9df571 --- /dev/null +++ b/analysis/lang/es/spanish_normalize_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2017 Couchbase, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package es + +import ( + "reflect" + "testing" + + "github.com/blevesearch/bleve/v2/analysis" +) + +func TestSpanishNormalizeFilter(t *testing.T) { + tests := []struct { + input analysis.TokenStream + output analysis.TokenStream + }{ + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Guía"), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Guia"), + }, + }, + }, + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Belcebú"), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Belcebu"), + }, + }, + }, + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Limón"), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("Limon"), + }, + }, + }, + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("agüero"), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("aguero"), + }, + }, + }, + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("laúd"), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte("laud"), + }, + }, + }, + // empty + { + input: analysis.TokenStream{ + &analysis.Token{ + Term: []byte(""), + }, + }, + output: analysis.TokenStream{ + &analysis.Token{ + Term: []byte(""), + }, + }, + }, + } + + spanishNormalizeFilter := NewSpanishNormalizeFilter() + for _, test := range tests { + actual := spanishNormalizeFilter.Filter(test.input) + if !reflect.DeepEqual(actual, test.output) { + t.Errorf("expected %#v, got %#v", test.output, actual) + t.Errorf("expected %s(% x), got %s(% x)", test.output[0].Term, test.output[0].Term, actual[0].Term, actual[0].Term) + } + } +}