diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh
index 903d578da..b8687dc56 100755
--- a/docker/docker-entrypoint.sh
+++ b/docker/docker-entrypoint.sh
@@ -122,27 +122,38 @@ install_languages() {
if [ ${#langs[@]} -eq 0 ]; then
return
fi
- apt-get update
+ # Build list of packages to install
+ to_install=()
for lang in "${langs[@]}"; do
pkg="tesseract-ocr-$lang"
if dpkg --status "$pkg" &>/dev/null; then
echo "Package $pkg already installed!"
continue
- fi
-
- if ! apt-cache show "$pkg" &>/dev/null; then
- echo "Package $pkg not found! :("
- continue
- fi
-
- echo "Installing package $pkg..."
- if ! apt-get --assume-yes install "$pkg" &>/dev/null; then
- echo "Could not install $pkg"
- exit 1
+ else
+ to_install+=("$pkg")
fi
done
+
+ # Use apt only when we install packages
+ if [ ${#to_install[@]} -gt 0 ]; then
+ apt-get update
+
+ for pkg in "${to_install[@]}"; do
+
+ if ! apt-cache show "$pkg" &>/dev/null; then
+ echo "Skipped $pkg: Package not found! :("
+ continue
+ fi
+
+ echo "Installing package $pkg..."
+ if ! apt-get --assume-yes install "$pkg" &>/dev/null; then
+ echo "Could not install $pkg"
+ exit 1
+ fi
+ done
+ fi
}
echo "Paperless-ngx docker container starting..."
diff --git a/docs/api.md b/docs/api.md
index 7e27f65d1..10b6799ab 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -236,12 +236,6 @@ results:
Pagination works exactly the same as it does for normal requests on this
endpoint.
-Certain limitations apply to full text queries:
-
-- Results are always sorted by search score. The results matching the
- query best will show up first.
-- Only a small subset of filtering parameters are supported.
-
Furthermore, each returned document has an additional `__search_hit__`
attribute with various information about the search results:
@@ -281,6 +275,67 @@ attribute with various information about the search results:
- `rank` is the index of the search results. The first result will
have rank 0.
+### Filtering by custom fields
+
+You can filter documents by their custom field values by specifying the
+`custom_field_lookup` query parameter. Here are some recipes for common
+use cases:
+
+1. Documents with a custom field "due" (date) between Aug 1, 2024 and
+ Sept 1, 2024 (inclusive):
+
+ `?custom_field_lookup=["due", "range", ["2024-08-01", "2024-09-01"]]`
+
+2. Documents with a custom field "customer" (text) that equals "bob"
+ (case sensitive):
+
+ `?custom_field_lookup=["customer", "exact", "bob"]`
+
+3. Documents with a custom field "answered" (boolean) set to `true`:
+
+ `?custom_field_lookup=["answered", "exact", true]`
+
+4. Documents with a custom field "favorite animal" (select) set to either
+ "cat" or "dog":
+
+ `?custom_field_lookup=["favorite animal", "in", ["cat", "dog"]]`
+
+5. Documents with a custom field "address" (text) that is empty:
+
+ `?custom_field_lookup=["OR", ["address", "isnull", true], ["address", "exact", ""]]`
+
+6. Documents that don't have a field called "foo":
+
+ `?custom_field_lookup=["foo", "exists", false]`
+
+7. Documents that have document links "references" to both document 3 and 7:
+
+ `?custom_field_lookup=["references", "contains", [3, 7]]`
+
+All field types support basic operations including `exact`, `in`, `isnull`,
+and `exists`. String, URL, and monetary fields support case-insensitive
+substring matching operations including `icontains`, `istartswith`, and
+`iendswith`. Integer, float, and date fields support arithmetic comparisons
+including `gt` (>), `gte` (>=), `lt` (<), `lte` (<=), and `range`.
+Lastly, document link fields support a `contains` operator that behaves
+like a "is superset of" check.
+
+!!! warning
+
+ It is possible to do case-insensitive exact match (i.e., `iexact`) and
+ case-sensitive substring match (i.e., `contains`, `startswith`,
+ `endswith`) for string, URL, and monetary fields, but
+ [they may not work as expected on some database backends](https://docs.djangoproject.com/en/5.1/ref/databases/#substring-matching-and-case-sensitivity).
+
+ It is also possible to use regular expressions to match string, URL, and
+ monetary fields, but the syntax is database-dependent, and accepting
+ regular expressions from untrusted sources could make your instance
+ vulnerable to regular expression denial of service attacks.
+
+ For these reasons the above expressions are disabled by default.
+ If you understand the implications, you may enable them by uncommenting
+ `PAPERLESS_CUSTOM_FIELD_LOOKUP_OPT_IN` in your configuration file.
+
### `/api/search/autocomplete/`
Get auto completions for a partial search term.
diff --git a/paperless.conf.example b/paperless.conf.example
index 63ee7be22..5fabbf390 100644
--- a/paperless.conf.example
+++ b/paperless.conf.example
@@ -81,6 +81,7 @@
#PAPERLESS_THUMBNAIL_FONT_NAME=
#PAPERLESS_IGNORE_DATES=
#PAPERLESS_ENABLE_UPDATE_CHECK=
+#PAPERLESS_ALLOW_CUSTOM_FIELD_LOOKUP=iexact,contains,startswith,endswith,regex,iregex
# Tika settings
diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf
index 7c0ec396d..c64cef921 100644
--- a/src-ui/messages.xlf
+++ b/src-ui/messages.xlf
@@ -3094,22 +3094,6 @@
src/app/components/common/clearable-badge/clearable-badge.component.html2
-
- src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 38
-
-
- src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 59
-
-
- src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 104
-
-
- src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 125
- Are you sure?
@@ -3324,36 +3308,36 @@
src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 91
+ 89Aftersrc/app/components/common/dates-dropdown/dates-dropdown.component.html
- 34
+ 42src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 100
+ 106Beforesrc/app/components/common/dates-dropdown/dates-dropdown.component.html
- 55
+ 62src/app/components/common/dates-dropdown/dates-dropdown.component.html
- 121
+ 126Addedsrc/app/components/common/dates-dropdown/dates-dropdown.component.html
- 76
+ 74src/app/components/document-list/document-list.component.html
@@ -3372,28 +3356,28 @@
Last 7 dayssrc/app/components/common/dates-dropdown/dates-dropdown.component.ts
- 45
+ 48Last monthsrc/app/components/common/dates-dropdown/dates-dropdown.component.ts
- 50
+ 53Last 3 monthssrc/app/components/common/dates-dropdown/dates-dropdown.component.ts
- 55
+ 58Last yearsrc/app/components/common/dates-dropdown/dates-dropdown.component.ts
- 60
+ 63
@@ -4646,7 +4630,7 @@
Not assignedsrc/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
- 340
+ 343Filter drop down element to filter for documents with no correspondent/type/tag assigned
@@ -4654,7 +4638,7 @@
Open filtersrc/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
- 452
+ 455
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.scss b/src-ui/src/app/components/app-frame/app-frame.component.scss
index 7f9871b97..f5427c713 100644
--- a/src-ui/src/app/components/app-frame/app-frame.component.scss
+++ b/src-ui/src/app/components/app-frame/app-frame.component.scss
@@ -13,7 +13,7 @@
padding: 50px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
overflow-y: auto;
- --pngx-sidebar-width: 25%;
+ --pngx-sidebar-width: 100%;
max-width: var(--pngx-sidebar-width);
.sidebar-heading .spinner-border {
diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html
index 8991363d2..dcab4606d 100644
--- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html
+++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html
@@ -1,4 +1,4 @@
-