opensolr-haystack 0.3.1__tar.gz → 0.5.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.
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/PKG-INFO +29 -2
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/README.md +28 -1
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/haystack_integrations/document_stores/opensolr/client.py +62 -3
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/haystack_integrations/document_stores/opensolr/store.py +62 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/PKG-INFO +29 -2
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/pyproject.toml +1 -1
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/LICENSE +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/haystack_integrations/components/retrievers/opensolr/__init__.py +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/haystack_integrations/components/retrievers/opensolr/retriever.py +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/haystack_integrations/document_stores/opensolr/__init__.py +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/SOURCES.txt +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/dependency_links.txt +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/requires.txt +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/top_level.txt +0 -0
- {opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensolr-haystack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -145,10 +145,37 @@ Don't need vectors? Pure keyword search skips the embedding call entirely —
|
|
|
145
145
|
zero AI quota, and it works on **any** Opensolr index, including non-vector
|
|
146
146
|
ones and older Solr versions.
|
|
147
147
|
|
|
148
|
+
## Search by image
|
|
149
|
+
|
|
150
|
+
Search your index with a **photo** instead of a text query. Opensolr reads the
|
|
151
|
+
picture three ways — visual labels (what it depicts), OCR text (words printed on
|
|
152
|
+
it), and any barcode / QR code — and turns that into an ordinary search. Nothing
|
|
153
|
+
new is stored in Solr; the picture simply becomes words.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# What the picture reads as (labels, OCR text, barcodes) — no search yet:
|
|
157
|
+
read = store.image_to_words("photo.jpg") # path, bytes, or base64
|
|
158
|
+
# {'text': 'red running shoe', 'mode': 'clip',
|
|
159
|
+
# 'labels': ['running shoe', 'sneaker'], 'codes': ['0123456789012']}
|
|
160
|
+
|
|
161
|
+
# Search the index with the picture:
|
|
162
|
+
docs = store.search_by_image("photo.jpg", top_k=4) # engine picks the best reading
|
|
163
|
+
docs = store.search_by_image("photo.jpg", using="meaning") # visual labels
|
|
164
|
+
docs = store.search_by_image("photo.jpg", using="text") # only OCR text
|
|
165
|
+
docs = store.search_by_image("photo.jpg", using="code") # exact barcode / QR match
|
|
166
|
+
docs = store.search_by_image("photo.jpg", using="all") # labels + OCR + codes
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`search_by_image` forwards the same tuning as `search` — `hybrid`, `lexical`,
|
|
170
|
+
`alpha`, `fresh_bias`, `filters` — so an image query runs through the exact
|
|
171
|
+
hybrid pipeline a text query does.
|
|
172
|
+
|
|
148
173
|
## Your index schema
|
|
149
174
|
|
|
150
175
|
Documents follow the Opensolr document model (`title`, `description`, `text`,
|
|
151
|
-
`meta_*` custom fields).
|
|
176
|
+
`meta_*` custom fields). The whole schema, every field and every type suffix,
|
|
177
|
+
is explained in the [Index Schema Reference](https://opensolr.com/opensolr-platform-user-documentation/schema-reference).
|
|
178
|
+
To see your own copy: **Control Panel → click your
|
|
152
179
|
index → Configuration → Edit File → schema.xml**. Prefer zero-effort data
|
|
153
180
|
entry? Configure the **Web Crawler** in the Control Panel (Index Tools →
|
|
154
181
|
WebCrawler): add your site URL, validate it, and Opensolr indexes the whole
|
|
@@ -127,10 +127,37 @@ Don't need vectors? Pure keyword search skips the embedding call entirely —
|
|
|
127
127
|
zero AI quota, and it works on **any** Opensolr index, including non-vector
|
|
128
128
|
ones and older Solr versions.
|
|
129
129
|
|
|
130
|
+
## Search by image
|
|
131
|
+
|
|
132
|
+
Search your index with a **photo** instead of a text query. Opensolr reads the
|
|
133
|
+
picture three ways — visual labels (what it depicts), OCR text (words printed on
|
|
134
|
+
it), and any barcode / QR code — and turns that into an ordinary search. Nothing
|
|
135
|
+
new is stored in Solr; the picture simply becomes words.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# What the picture reads as (labels, OCR text, barcodes) — no search yet:
|
|
139
|
+
read = store.image_to_words("photo.jpg") # path, bytes, or base64
|
|
140
|
+
# {'text': 'red running shoe', 'mode': 'clip',
|
|
141
|
+
# 'labels': ['running shoe', 'sneaker'], 'codes': ['0123456789012']}
|
|
142
|
+
|
|
143
|
+
# Search the index with the picture:
|
|
144
|
+
docs = store.search_by_image("photo.jpg", top_k=4) # engine picks the best reading
|
|
145
|
+
docs = store.search_by_image("photo.jpg", using="meaning") # visual labels
|
|
146
|
+
docs = store.search_by_image("photo.jpg", using="text") # only OCR text
|
|
147
|
+
docs = store.search_by_image("photo.jpg", using="code") # exact barcode / QR match
|
|
148
|
+
docs = store.search_by_image("photo.jpg", using="all") # labels + OCR + codes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`search_by_image` forwards the same tuning as `search` — `hybrid`, `lexical`,
|
|
152
|
+
`alpha`, `fresh_bias`, `filters` — so an image query runs through the exact
|
|
153
|
+
hybrid pipeline a text query does.
|
|
154
|
+
|
|
130
155
|
## Your index schema
|
|
131
156
|
|
|
132
157
|
Documents follow the Opensolr document model (`title`, `description`, `text`,
|
|
133
|
-
`meta_*` custom fields).
|
|
158
|
+
`meta_*` custom fields). The whole schema, every field and every type suffix,
|
|
159
|
+
is explained in the [Index Schema Reference](https://opensolr.com/opensolr-platform-user-documentation/schema-reference).
|
|
160
|
+
To see your own copy: **Control Panel → click your
|
|
134
161
|
index → Configuration → Edit File → schema.xml**. Prefer zero-effort data
|
|
135
162
|
entry? Configure the **Web Crawler** in the Control Panel (Index Tools →
|
|
136
163
|
WebCrawler): add your site URL, validate it, and Opensolr indexes the whole
|
|
@@ -66,6 +66,41 @@ BATCH_EMBED_MAX = 50
|
|
|
66
66
|
# so it is applied at full strength with no hedging.
|
|
67
67
|
FRESH_BIAS_FUNCTION = "recip(max(0,ms(NOW,creation_date)),3.16e-11,1,1)"
|
|
68
68
|
|
|
69
|
+
#: Default Fresh Results Bias strength when a caller does not pass one.
|
|
70
|
+
FRESH_BIAS_WEIGHT_DEFAULT = 0.5
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def fresh_bias_function(weight: Optional[float] = None) -> str:
|
|
74
|
+
"""Build the recency function for a 0.0-1.0 ``weight``.
|
|
75
|
+
|
|
76
|
+
Mirrors ``Hybrid_search::fresh_bias_function()`` on opensolr.com, the Drupal module and
|
|
77
|
+
the WordPress plugin. ``recip(ms, c, 1, 1)`` halves at ``ms = 1/c``, so the weight is a
|
|
78
|
+
HALF-LIFE on a geometric scale between 365 days at 0.0 and 6 hours at 1.0:
|
|
79
|
+
|
|
80
|
+
========== ===========================================================
|
|
81
|
+
weight meaning
|
|
82
|
+
========== ===========================================================
|
|
83
|
+
0.0 365-day half-life: technically on, barely visible
|
|
84
|
+
0.3 41 days
|
|
85
|
+
0.5 9.6 days (the default)
|
|
86
|
+
0.7 2.2 days
|
|
87
|
+
1.0 6 hours: date all but replaces relevance
|
|
88
|
+
========== ===========================================================
|
|
89
|
+
|
|
90
|
+
The fixed constant this replaces behaved like 0.0, which is why Fresh looked broken on a
|
|
91
|
+
news index: a 10-day-old article kept 97% of its multiplier, nowhere near enough to
|
|
92
|
+
outrank a better-matching older one.
|
|
93
|
+
"""
|
|
94
|
+
w = FRESH_BIAS_WEIGHT_DEFAULT if weight is None else float(weight)
|
|
95
|
+
w = max(0.0, min(1.0, w))
|
|
96
|
+
half_life_days = 365.0 * (0.25 / 365.0) ** w
|
|
97
|
+
# PHP's %g writes "1.212e-9" where Python's writes "1.212e-09". Numerically identical,
|
|
98
|
+
# textually not — and this string is compared byte for byte against the platform, the
|
|
99
|
+
# Drupal module and the WordPress plugin by the prompt/query parity harness. Strip the
|
|
100
|
+
# padding zero so all five implementations emit the same characters.
|
|
101
|
+
c = ("%.4g" % (1.0 / (half_life_days * 86400000.0))).replace("e-0", "e-").replace("e+0", "e+")
|
|
102
|
+
return "recip(max(0,ms(NOW,creation_date))," + c + ",1,1)"
|
|
103
|
+
|
|
69
104
|
#: The four candidate-selection modes the {!hybrid} parser understands.
|
|
70
105
|
#:
|
|
71
106
|
#: Validated rather than trusted, because the failure is silent: `mode` is interpolated into
|
|
@@ -77,7 +112,7 @@ FRESH_BIAS_FUNCTION = "recip(max(0,ms(NOW,creation_date)),3.16e-11,1,1)"
|
|
|
77
112
|
HYBRID_MODES = ("union", "keywords_required", "meaning_required", "intersection")
|
|
78
113
|
|
|
79
114
|
|
|
80
|
-
def apply_fresh_bias(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
115
|
+
def apply_fresh_bias(params: Dict[str, Any], weight: Optional[float] = None) -> Dict[str, Any]:
|
|
81
116
|
"""Wrap an already-built ``params["q"]`` so the recency curve multiplies the
|
|
82
117
|
FINAL score. Mutates ``params`` in place and returns it.
|
|
83
118
|
|
|
@@ -99,7 +134,21 @@ def apply_fresh_bias(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
99
134
|
rather than being inlined, so a ``}`` in the user's text cannot close the
|
|
100
135
|
``{!boost}`` block and leave the remainder to be parsed as query syntax.
|
|
101
136
|
"""
|
|
102
|
-
|
|
137
|
+
# A document with no creation_date evaluates recip() at its MAXIMUM, 1.0 — Solr's
|
|
138
|
+
# ms(NOW, <missing>) is 0 — so an undated document is scored as if published this
|
|
139
|
+
# instant and floats to the top of a "newest first" ranking. Require a date instead
|
|
140
|
+
# of silently promoting the ones that have none.
|
|
141
|
+
fq = params.get("fq")
|
|
142
|
+
date_fq = "+creation_date:[* TO *]"
|
|
143
|
+
if fq is None:
|
|
144
|
+
params["fq"] = date_fq
|
|
145
|
+
elif isinstance(fq, list):
|
|
146
|
+
if date_fq not in fq:
|
|
147
|
+
params["fq"] = fq + [date_fq]
|
|
148
|
+
elif fq != date_fq:
|
|
149
|
+
params["fq"] = [fq, date_fq]
|
|
150
|
+
|
|
151
|
+
params["freshBias"] = fresh_bias_function(weight)
|
|
103
152
|
params["freshBiasInner"] = params["q"]
|
|
104
153
|
params["q"] = "{!boost b=$freshBias v=$freshBiasInner}"
|
|
105
154
|
return params
|
|
@@ -491,6 +540,15 @@ class OpensolrClient:
|
|
|
491
540
|
def ai(self, method: str, **params: Any) -> Any:
|
|
492
541
|
return self._request(AI_BASE, method, params)
|
|
493
542
|
|
|
543
|
+
def image_to_text(self, index: str, image_b64: str, top_k: int = 8) -> Dict[str, Any]:
|
|
544
|
+
"""Turn an image into search words via the Opensolr image_to_text API.
|
|
545
|
+
|
|
546
|
+
The answer carries ``text`` (the words to search with), ``mode`` ("clip"/"ocr"),
|
|
547
|
+
``labels`` (CLIP visual labels, present even in OCR mode) and ``codes``
|
|
548
|
+
(barcodes / QR codes). No image vector is stored: the picture becomes words.
|
|
549
|
+
"""
|
|
550
|
+
return self.ai("image_to_text", index_name=index, image=image_b64, top_k=top_k)
|
|
551
|
+
|
|
494
552
|
# ------------------------------------------------------------------ #
|
|
495
553
|
# management #
|
|
496
554
|
# ------------------------------------------------------------------ #
|
|
@@ -706,6 +764,7 @@ class OpensolrClient:
|
|
|
706
764
|
fl: str = "*,score",
|
|
707
765
|
fq: Optional[str] = None,
|
|
708
766
|
fresh_bias: bool = False,
|
|
767
|
+
fresh_bias_weight: Optional[float] = None,
|
|
709
768
|
) -> Dict[str, Any]:
|
|
710
769
|
"""Hybrid (BM25 + kNN) search via the native ``{!hybrid}`` parser.
|
|
711
770
|
|
|
@@ -745,7 +804,7 @@ class OpensolrClient:
|
|
|
745
804
|
if fq:
|
|
746
805
|
params["fq"] = fq
|
|
747
806
|
if fresh_bias:
|
|
748
|
-
apply_fresh_bias(params)
|
|
807
|
+
apply_fresh_bias(params, fresh_bias_weight)
|
|
749
808
|
return self.solr_select(index, params)
|
|
750
809
|
|
|
751
810
|
#: RAG context defaults — how many hybrid hits feed the LLM, and how many
|
|
@@ -7,6 +7,8 @@ local embedder component needed at indexing time.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import base64
|
|
11
|
+
from pathlib import Path
|
|
10
12
|
import json
|
|
11
13
|
import re
|
|
12
14
|
from typing import Any, Dict, List, Optional
|
|
@@ -376,6 +378,66 @@ class OpensolrDocumentStore:
|
|
|
376
378
|
# serialization #
|
|
377
379
|
# ------------------------------------------------------------------ #
|
|
378
380
|
|
|
381
|
+
def image_to_words(self, image, top_k: int = 8) -> Dict[str, Any]:
|
|
382
|
+
"""Read an image and return what it was turned into, without searching yet.
|
|
383
|
+
|
|
384
|
+
Returns ``{"text", "mode", "labels", "codes"}``: the best words to search
|
|
385
|
+
with, whether it was read as visual labels ("clip") or OCR text ("ocr"),
|
|
386
|
+
the CLIP visual labels (present even in OCR mode), and any barcodes / QR codes.
|
|
387
|
+
``image`` is a file path (str/Path) or raw image bytes.
|
|
388
|
+
"""
|
|
389
|
+
raw = bytes(image) if isinstance(image, (bytes, bytearray)) else Path(image).read_bytes()
|
|
390
|
+
b64 = base64.b64encode(raw).decode("ascii")
|
|
391
|
+
ans = self.client.image_to_text(self._index, b64, top_k=top_k)
|
|
392
|
+
labels = [l["label"] for l in (ans.get("labels") or []) if isinstance(l, dict) and l.get("label")]
|
|
393
|
+
codes = []
|
|
394
|
+
for c in (ans.get("codes") or []):
|
|
395
|
+
text = c.get("text") if isinstance(c, dict) else (c if isinstance(c, str) else None)
|
|
396
|
+
if text:
|
|
397
|
+
codes.append(text)
|
|
398
|
+
return {"text": (ans.get("text") or "").strip(), "mode": ans.get("mode") or "clip", "labels": labels, "codes": codes}
|
|
399
|
+
|
|
400
|
+
def search_by_image(
|
|
401
|
+
self,
|
|
402
|
+
image,
|
|
403
|
+
top_k: int = 10,
|
|
404
|
+
using: str = "auto",
|
|
405
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
406
|
+
image_top_k: int = 8,
|
|
407
|
+
**search_kwargs,
|
|
408
|
+
) -> List[Document]:
|
|
409
|
+
"""Search the index with a photo instead of a text query.
|
|
410
|
+
|
|
411
|
+
The image is turned into words by :meth:`image_to_words`, then those words
|
|
412
|
+
run through the normal :meth:`search`, so ``hybrid``, ``lexical``, ``alpha``
|
|
413
|
+
and ``fresh_bias`` apply via ``search_kwargs``. No image vector is stored.
|
|
414
|
+
|
|
415
|
+
``using``: which reading to search with — ``"auto"`` (the engine's chosen
|
|
416
|
+
text), ``"meaning"`` (CLIP visual labels), ``"text"`` (OCR text only), or
|
|
417
|
+
``"code"`` (the first barcode / QR code), or ``"all"`` (labels + OCR text + barcodes combined).
|
|
418
|
+
"""
|
|
419
|
+
read = self.image_to_words(image, top_k=image_top_k)
|
|
420
|
+
if using == "meaning":
|
|
421
|
+
query = ", ".join(read["labels"])
|
|
422
|
+
elif using == "text":
|
|
423
|
+
query = read["text"] if read["mode"] == "ocr" else ""
|
|
424
|
+
elif using == "code":
|
|
425
|
+
query = read["codes"][0] if read["codes"] else ""
|
|
426
|
+
search_kwargs.setdefault("lexical", True)
|
|
427
|
+
elif using == "all":
|
|
428
|
+
parts = list(read["labels"])
|
|
429
|
+
if read["mode"] == "ocr" and read["text"]:
|
|
430
|
+
parts.append(read["text"])
|
|
431
|
+
parts.extend(read["codes"])
|
|
432
|
+
query = ", ".join(p for p in parts if p)
|
|
433
|
+
elif using == "auto":
|
|
434
|
+
query = read["text"]
|
|
435
|
+
else:
|
|
436
|
+
raise ValueError("using must be one of 'auto', 'meaning', 'text', 'code', 'all'")
|
|
437
|
+
if not query:
|
|
438
|
+
return []
|
|
439
|
+
return self.search(query, top_k=top_k, filters=filters, **search_kwargs)
|
|
440
|
+
|
|
379
441
|
def to_dict(self) -> Dict[str, Any]:
|
|
380
442
|
return default_to_dict(
|
|
381
443
|
self,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensolr-haystack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -145,10 +145,37 @@ Don't need vectors? Pure keyword search skips the embedding call entirely —
|
|
|
145
145
|
zero AI quota, and it works on **any** Opensolr index, including non-vector
|
|
146
146
|
ones and older Solr versions.
|
|
147
147
|
|
|
148
|
+
## Search by image
|
|
149
|
+
|
|
150
|
+
Search your index with a **photo** instead of a text query. Opensolr reads the
|
|
151
|
+
picture three ways — visual labels (what it depicts), OCR text (words printed on
|
|
152
|
+
it), and any barcode / QR code — and turns that into an ordinary search. Nothing
|
|
153
|
+
new is stored in Solr; the picture simply becomes words.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# What the picture reads as (labels, OCR text, barcodes) — no search yet:
|
|
157
|
+
read = store.image_to_words("photo.jpg") # path, bytes, or base64
|
|
158
|
+
# {'text': 'red running shoe', 'mode': 'clip',
|
|
159
|
+
# 'labels': ['running shoe', 'sneaker'], 'codes': ['0123456789012']}
|
|
160
|
+
|
|
161
|
+
# Search the index with the picture:
|
|
162
|
+
docs = store.search_by_image("photo.jpg", top_k=4) # engine picks the best reading
|
|
163
|
+
docs = store.search_by_image("photo.jpg", using="meaning") # visual labels
|
|
164
|
+
docs = store.search_by_image("photo.jpg", using="text") # only OCR text
|
|
165
|
+
docs = store.search_by_image("photo.jpg", using="code") # exact barcode / QR match
|
|
166
|
+
docs = store.search_by_image("photo.jpg", using="all") # labels + OCR + codes
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`search_by_image` forwards the same tuning as `search` — `hybrid`, `lexical`,
|
|
170
|
+
`alpha`, `fresh_bias`, `filters` — so an image query runs through the exact
|
|
171
|
+
hybrid pipeline a text query does.
|
|
172
|
+
|
|
148
173
|
## Your index schema
|
|
149
174
|
|
|
150
175
|
Documents follow the Opensolr document model (`title`, `description`, `text`,
|
|
151
|
-
`meta_*` custom fields).
|
|
176
|
+
`meta_*` custom fields). The whole schema, every field and every type suffix,
|
|
177
|
+
is explained in the [Index Schema Reference](https://opensolr.com/opensolr-platform-user-documentation/schema-reference).
|
|
178
|
+
To see your own copy: **Control Panel → click your
|
|
152
179
|
index → Configuration → Edit File → schema.xml**. Prefer zero-effort data
|
|
153
180
|
entry? Configure the **Web Crawler** in the Control Panel (Index Tools →
|
|
154
181
|
WebCrawler): add your site URL, validate it, and Opensolr indexes the whole
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opensolr-haystack"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Haystack integration for Opensolr — managed Apache Solr DocumentStore with server-side embeddings and hybrid BM25+kNN retrieval"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{opensolr_haystack-0.3.1 → opensolr_haystack-0.5.0}/opensolr_haystack.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|