opensolr-haystack 0.2.8__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/PKG-INFO +28 -1
  2. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/README.md +27 -0
  3. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/haystack_integrations/components/retrievers/opensolr/retriever.py +17 -1
  4. opensolr_haystack-0.3.0/haystack_integrations/document_stores/opensolr/client.py +895 -0
  5. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/haystack_integrations/document_stores/opensolr/store.py +43 -3
  6. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/opensolr_haystack.egg-info/PKG-INFO +28 -1
  7. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/pyproject.toml +1 -1
  8. opensolr_haystack-0.2.8/haystack_integrations/document_stores/opensolr/client.py +0 -469
  9. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/LICENSE +0 -0
  10. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/haystack_integrations/components/retrievers/opensolr/__init__.py +0 -0
  11. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/haystack_integrations/document_stores/opensolr/__init__.py +0 -0
  12. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/opensolr_haystack.egg-info/SOURCES.txt +0 -0
  13. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/opensolr_haystack.egg-info/dependency_links.txt +0 -0
  14. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/opensolr_haystack.egg-info/requires.txt +0 -0
  15. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/opensolr_haystack.egg-info/top_level.txt +0 -0
  16. {opensolr_haystack-0.2.8 → opensolr_haystack-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensolr-haystack
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: Haystack integration for Opensolr — managed Apache Solr DocumentStore with server-side embeddings and hybrid BM25+kNN retrieval
5
5
  Author-email: Opensolr <support@opensolr.com>
6
6
  License: MIT
@@ -157,6 +157,33 @@ tuning={"search_mode": "keywords_required", "fw_title": 0.2,
157
157
  Defaults match the platform's PHP configuration exactly — customize in the
158
158
  Control Panel once, or per call from code.
159
159
 
160
+ #### Fresh Results Bias
161
+
162
+ Rank newer documents higher without hiding anything older. Every score is
163
+ multiplied by a recency curve on `creation_date` — full weight for a document
164
+ published today, about half after a year:
165
+
166
+ ```python
167
+ store.similarity_search_with_score("solar inverter warranty", fresh_bias=True)
168
+ client.hybrid_search(index, query, fresh_bias=True)
169
+ client.ai_answer(index, question, tuning={"fresh_bias": 1})
170
+ ```
171
+
172
+ It **re-orders and never filters**: the hit count is identical either way,
173
+ nothing old becomes unreachable, and a document with no `creation_date` simply
174
+ keeps its place instead of being pushed to the bottom. It applies to all three
175
+ retrieval shapes — vector-only, keyword-only and the fused hybrid ranking —
176
+ because the boost wraps the final score rather than one half of it. Off by
177
+ default.
178
+
179
+ This is the same control visitors get as the **Fresh** toggle beside the sort
180
+ options on the hosted Opensolr search page, so a query behaves identically here
181
+ and there.
182
+
183
+ > `fresh_bias` and `freshness_boost` are two different knobs and the names
184
+ > invite confusion. `freshness_boost` is a hard window in **days** — anything
185
+ > older is filtered out and the hit count drops. `fresh_bias` filters nothing.
186
+
160
187
  ## How it's tested
161
188
 
162
189
  Every release is validated against **live Opensolr infrastructure** — no mocks:
@@ -139,6 +139,33 @@ tuning={"search_mode": "keywords_required", "fw_title": 0.2,
139
139
  Defaults match the platform's PHP configuration exactly — customize in the
140
140
  Control Panel once, or per call from code.
141
141
 
142
+ #### Fresh Results Bias
143
+
144
+ Rank newer documents higher without hiding anything older. Every score is
145
+ multiplied by a recency curve on `creation_date` — full weight for a document
146
+ published today, about half after a year:
147
+
148
+ ```python
149
+ store.similarity_search_with_score("solar inverter warranty", fresh_bias=True)
150
+ client.hybrid_search(index, query, fresh_bias=True)
151
+ client.ai_answer(index, question, tuning={"fresh_bias": 1})
152
+ ```
153
+
154
+ It **re-orders and never filters**: the hit count is identical either way,
155
+ nothing old becomes unreachable, and a document with no `creation_date` simply
156
+ keeps its place instead of being pushed to the bottom. It applies to all three
157
+ retrieval shapes — vector-only, keyword-only and the fused hybrid ranking —
158
+ because the boost wraps the final score rather than one half of it. Off by
159
+ default.
160
+
161
+ This is the same control visitors get as the **Fresh** toggle beside the sort
162
+ options on the hosted Opensolr search page, so a query behaves identically here
163
+ and there.
164
+
165
+ > `fresh_bias` and `freshness_boost` are two different knobs and the names
166
+ > invite confusion. `freshness_boost` is a hard window in **days** — anything
167
+ > older is filtered out and the hit count drops. `fresh_bias` filters nothing.
168
+
142
169
  ## How it's tested
143
170
 
144
171
  Every release is validated against **live Opensolr infrastructure** — no mocks:
@@ -38,6 +38,7 @@ class OpensolrHybridRetriever:
38
38
  alpha: float = 0.5,
39
39
  filters: Optional[Dict[str, Any]] = None,
40
40
  lexical: bool = False,
41
+ fresh_bias: bool = False,
41
42
  ) -> None:
42
43
  self.document_store = document_store
43
44
  self.top_k = top_k
@@ -45,6 +46,11 @@ class OpensolrHybridRetriever:
45
46
  self.alpha = alpha
46
47
  self.filters = filters
47
48
  self.lexical = lexical
49
+ # Threaded through the component because a pipeline never touches the store
50
+ # directly — run() below is the only door onto OpensolrDocumentStore.search()
51
+ # for a Haystack user, so an option the component does not carry is an option
52
+ # that does not exist in a pipeline.
53
+ self.fresh_bias = fresh_bias
48
54
 
49
55
  @component.output_types(documents=List[Document])
50
56
  def run(
@@ -55,9 +61,14 @@ class OpensolrHybridRetriever:
55
61
  alpha: Optional[float] = None,
56
62
  filters: Optional[Dict[str, Any]] = None,
57
63
  lexical: Optional[bool] = None,
64
+ fresh_bias: Optional[bool] = None,
58
65
  ) -> Dict[str, List[Document]]:
59
66
  """Run the retriever. ``alpha``: 0 = all semantic, 1 = all lexical.
60
- ``lexical=True`` = pure keyword search, no embedding call."""
67
+ ``lexical=True`` = pure keyword search, no embedding call.
68
+ ``fresh_bias=True`` biases the ranking toward recent documents by
69
+ multiplying each score by a recency curve on ``creation_date``; it
70
+ re-orders and never filters, so the hit count is unchanged and nothing
71
+ old becomes unreachable. Off by default."""
61
72
  docs = self.document_store.search(
62
73
  query=query,
63
74
  top_k=top_k if top_k is not None else self.top_k,
@@ -65,6 +76,7 @@ class OpensolrHybridRetriever:
65
76
  alpha=alpha if alpha is not None else self.alpha,
66
77
  filters=filters if filters is not None else self.filters,
67
78
  lexical=lexical if lexical is not None else self.lexical,
79
+ fresh_bias=fresh_bias if fresh_bias is not None else self.fresh_bias,
68
80
  )
69
81
  return {"documents": docs}
70
82
 
@@ -77,6 +89,10 @@ class OpensolrHybridRetriever:
77
89
  alpha=self.alpha,
78
90
  filters=self.filters,
79
91
  lexical=self.lexical,
92
+ # Serialized alongside the other search options so a pipeline saved with
93
+ # the bias on comes back with it on. from_dict() tolerates its absence in
94
+ # dicts written before this option existed — the constructor defaults it.
95
+ fresh_bias=self.fresh_bias,
80
96
  )
81
97
 
82
98
  @classmethod