This document describes the v2 Tags API of Fess, with which logged-in users manage their own tags and put them on documents. For the common response envelope, error model, and CSRF, see API Overview.
The base URL is http://<Server Name>/api/v2/ (local environment example: http://localhost:8080/api/v2).
Note
Tags are disabled by default. To use them, set user.tag.enabled=true in fess_config.properties. features.user_tag of /api/v2/ui/config reports the state. While tags are disabled, a request that passes the CSRF and Origin checks and uses a supported method gets invalid_request (400) from the tag endpoints.
How Tags Work
Tags are kept per user. The user who creates a tag owns it, and the owner is the login user ID. Two users can use the same name and still have two separate tags.
Only logged-in users can use tags. Every tag endpoint acts as the user of the login session; an access token does not stand in for a login. A caller without a login gets
auth_required(401).A new tag is private: only its owner sees it. When the owner shares the tag, every logged-in user can see it and filter by it. A user who is not logged in sees no tag, shared or not.
Only the owner can change or delete a tag, or put it on and take it off documents. A shared tag of another user can only be shown and filtered on. Administrators manage all tags on the admin screen (see Tag).
A tag is put on the URL of a document, so every indexed document with that URL gets it. The exception is a tag that has a virtual host: it is put only on the documents of crawl settings that have that virtual host, so the same URL crawled for two virtual hosts is two documents. A tag that has no virtual host, and a document that has no virtual host, behave as before (see Virtual Hosts below).
Each tag has two identifiers.
valueThe tag value,
base64url(name):base64url(owner)(UTF-8, without padding), stored in thetagfield of the index. Treat it as an opaque value to filter search results with.idThe tag ID, the SHA-256 of
valuein lowercase hex (64 characters). It goes into paths such as/api/v2/tags/{tagId}. Renaming a tag changes bothvalueandid.
A tag name is NFKC-normalized, runs of whitespace are collapsed to one space and the ends are trimmed. The result must be 1 to user.tag.name.max.length (default: 50) characters, and names with a control or format character (such as a zero-width character or a bidi override) are refused.
Virtual Hosts
When virtual hosts are configured, tags follow the virtual host of the request, as the tag facet does.
A tag records the virtual host of the request that created it.
On a request that matches a virtual host,
GET /api/v2/tagsand theaddablefield of the document tag endpoints list only the caller’s tags of that virtual host. A tag of another virtual host, or a tag that has no virtual host, is not listed. Renaming it, deleting it or putting it on a document byidanswersnot_found(404). Putting it on a document bynameanswersconflict(409), because a caller has one tag of each name across virtual hosts. A request that matches no virtual host sees every tag.A tag of a virtual host is set on a document only when the crawl settings of the document have that virtual host. This applies when the document is indexed again, when the
tag_updaterjob runs and when the tag is put on the document. A tag that has no virtual host, and a document that has no virtual host, are not limited.An administrator can change the virtual host of a tag, or delete the tag, on the Tag Type screen (see Tag).
Tags in Search
While user.tag.enabled is true, the search API (/api/v2/search) handles tags as follows.
Each hit carries the tags that the caller can see as
tags. Each entry hasvalue,name,owner,mine(truewhen the caller owns the tag) andshared(truefor a shared tag).tagsis absent when there is none. Thetagindex field itself is never returned.facet.field=tagreturns infacet_fielda facet of the tags that the caller can see. Besidesvalueandcount, each bucket haslabel(the tag name),owner,mineandshared.fields.tag=<value>narrows the results to the documents with a tag. Pass thevalueof a hit’stagsor of a facet bucket as is.
A condition on tags (fields.tag, tag:, ex_q, facet.query) matches only an exact value of a tag that the caller can see. The value of a tag the caller cannot see, and wildcard, prefix, fuzzy and range conditions, match nothing. A caller without a login gets no tags and no tag facet, and a tag condition matches nothing.
The value of a tag contains :, so quote it in a query: q=tag:"<value>" works. Without the quotes, q=tag:<value> matches nothing and ex_q=tag:<value> is refused with HTTP 400.
One user sees at most user.tag.visible.max.size (default: 1000) tags, the user’s own tags first. Tags beyond that do not appear in tags of the hits or in the facet, but can still be filtered on.
When Documents Reflect Changes
Creating, changing and deleting tags, and putting them on and taking them off documents, show in the tag endpoints at once. The tag field of the indexed documents, however, is updated through an in-memory queue that the “Log Aggregator” job (log_aggregator) applies in bulk every minute. The search hits, the facet and the filters therefore reflect a change after up to about a minute. After a rename, the documents keep the old value until the queue is next processed, and the tag does not show on them in the meantime.
For the queue and the jobs, see Tag.
Listing Tags
Request
| HTTP Method | GET |
| Endpoint | /api/v2/tags |
Returns the tags that the caller owns, by sort order and name. Shared tags of other users are not included.
Response
On success (200), the following fields are returned directly under response of the common envelope.
{
"response": {
"status": 0,
"tags": [
{
"id": "0698e7f9c98a9b64ef11a2bbc71d8d2668c6907b397fa86c23ba19b7fec02d6c",
"value": "dG8tcmV2aWV3:YWxpY2U",
"name": "to-review",
"shared": false,
"sort_order": 0,
"path_count": 3
}
]
}
}
tags | The caller’s tags. Each has |
Table: Response Fields
Creating a Tag
Request
| HTTP Method | POST |
| Endpoint | /api/v2/tags |
Creates a tag of the caller. As a state-changing request, it requires the X-Fess-CSRF-Token header (see API Overview).
A user can have at most
user.tag.max.tags(default:1000) tags. Beyond that, the endpoint answersinvalid_request(400).When the caller already has a tag of the name, the endpoint answers
conflict(409). Another user having a tag of the same name does not matter.
Send Content-Type: application/json; the body can be at most 1 KiB (1024 bytes).
{
"name": "to-review",
"shared": false
}
name | Tag name (str, required). |
shared | true lets every logged-in user see the tag (bool, default: false). |
Table: Request Body
Response
On success (200), tag directly under response holds the new tag, in the form of an entry of GET /api/v2/tags with a path_count of 0.
Changing a Tag
Request
| HTTP Method | PUT |
| Endpoint | /api/v2/tags/{tagId} |
Renames a tag of the caller or changes whether it is shared. The X-Fess-CSRF-Token header is required.
The body has
name,sharedor both.A new
namerenames the tag, which gives it a newidandvalue. The documents with the old value get the new one when the queue is next processed. Renaming to a name the caller already uses answersconflict(409) and leaves the tag unchanged.sharedonly changes who sees the tag; no document is updated. Settingsharedtofalsekeeps the roles and groups an administrator added to the permissions.A shared tag of another user answers
forbidden(403), and a tag the caller cannot seenot_found(404).A write that keeps losing a race with another update also answers
conflict(409).
{
"name": "reviewed",
"shared": true
}
On success (200), response holds tag (the tag as changed) and renamed (true when the tag was renamed, in which case tag.id and tag.value are new).
Deleting a Tag
Request
| HTTP Method | DELETE |
| Endpoint | /api/v2/tags/{tagId} |
Deletes a tag of the caller. The documents lose its value when the queue is next processed. The X-Fess-CSRF-Token header is required. A shared tag of another user answers forbidden (403), and a tag the caller cannot see not_found (404).
On success (200), response holds id (the deleted tag ID) and deleted (always true).
Getting the Tags of a Document
Request
| HTTP Method | GET |
| Endpoint | /api/v2/documents/{docId}/tags |
Returns the tags on the document’s URL that the caller can see, and the caller’s own tags that are not on it. The document is looked up with the caller’s roles, so a document the caller cannot search answers not_found (404).
Response
On success (200), the following fields are returned directly under response of the common envelope.
{
"response": {
"status": 0,
"doc_id": "a1b2c3d4e5f6",
"tags": [
{
"id": "0698e7f9c98a9b64ef11a2bbc71d8d2668c6907b397fa86c23ba19b7fec02d6c",
"value": "dG8tcmV2aWV3:YWxpY2U",
"name": "to-review",
"owner": "alice",
"mine": true,
"shared": false
},
{
"id": "41e429a7d0081e25390c3840268d736dca00250167bab94250389aee9e08e2ed",
"value": "c3BlY3M:Ym9i",
"name": "specs",
"owner": "bob",
"mine": false,
"shared": true
}
],
"addable": []
}
}
Table: Response Fields
Putting a Tag on a Document
Request
| HTTP Method | POST |
| Endpoint | /api/v2/documents/{docId}/tags |
Adds the document’s URL to a tag of the caller. The X-Fess-CSRF-Token header is required.
The body (Content-Type: application/json, at most 1 KiB) gives either id, an existing tag, or name, a tag name. When both are given, id wins.
{
"name": "to-review"
}
With
name, a private tag of the name is created and put on the document when the caller has none. The new tag counts againstuser.tag.max.tags.A tag can be on at most
user.tag.max.paths(default:10000) URLs. Beyond that, the endpoint answersinvalid_request(400).An
idof another user’s tag answersforbidden(403) when the caller can see the tag andnot_found(404) otherwise.On success, the response has the fields of Getting the Tags of a Document plus
addedandtag. The tag endpoints show the tag at once; the search results of the documents with the URL reflect it when the queue is next processed (about a minute later).
Taking a Tag off a Document
Request
| HTTP Method | DELETE |
| Endpoint | /api/v2/documents/{docId}/tags/{tagId} |
Removes the document’s URL from the caller’s tag given by tagId. The X-Fess-CSRF-Token header is required. Another user’s tag answers forbidden (403) when the caller can see it and not_found (404) otherwise. On success, the response has the fields of Getting the Tags of a Document plus removed.
Error Response
For details on the error model, see API Overview. The tag endpoints return the following HTTP statuses.
Table: Error Response
Settings
The following settings in fess_config.properties adjust tags.
| Property | Description | Default |
|---|---|---|
user.tag.enabled | Whether logged-in users can use tags. | false |
user.tag.name.max.length | Maximum length of a tag name, in code points. | 50 |
user.tag.max.tags | Maximum number of tags one user can own. | 1000 |
user.tag.max.paths | Maximum number of URLs one tag can be put on. | 10000 |
user.tag.queue.max.size | Maximum number of changes held in memory until they reach the documents. A change beyond it is dropped with a WARN log. | 10000 |
user.tag.process.batch.size | Number of URLs updated per bulk request when changes are applied to the documents. | 100 |
user.tag.visible.max.size | Maximum number of tags visible to one user in a search. | 1000 |