<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.archivematica.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jraddaoui</id>
	<title>Archivematica - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.archivematica.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jraddaoui"/>
	<link rel="alternate" type="text/html" href="https://wiki.archivematica.org/Special:Contributions/Jraddaoui"/>
	<updated>2026-05-02T19:22:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.4</generator>
	<entry>
		<id>https://wiki.archivematica.org/index.php?title=Elasticsearch_Development&amp;diff=12875</id>
		<title>Elasticsearch Development</title>
		<link rel="alternate" type="text/html" href="https://wiki.archivematica.org/index.php?title=Elasticsearch_Development&amp;diff=12875"/>
		<updated>2019-02-15T17:24:47Z</updated>

		<summary type="html">&lt;p&gt;Jraddaoui: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;From Archivematica 0.9, AIP package information, such as METS data, is indexed&lt;br /&gt;
using [http://www.elasticsearch.org/ Elasticsearch (ES)]. This data can be&lt;br /&gt;
searched from the Archival Storage area of the dashboard or can be interfaced&lt;br /&gt;
with programmatically. For Elasticsearch administration information, such as&lt;br /&gt;
how to delete an Elasticsearch index, please reference the&lt;br /&gt;
[[Administrator_manual_1.2#Elasticsearch|administrator manual]].&lt;br /&gt;
&lt;br /&gt;
'''NB.''' From Archivematica 1.7.0, users are given the option of whether to&lt;br /&gt;
index information using Elasticsearch, and so the information below might not&lt;br /&gt;
work. It will be dependant on how your Archivematica instance has been&lt;br /&gt;
configured.&lt;br /&gt;
&lt;br /&gt;
'''NB.''' In Archivematica 1.9.0, the Elasticsearch version support has been&lt;br /&gt;
upgraded from ES 1.x to the 6.x version. Check [https://wiki.archivematica.org/Elasticsearch_Development_1.9 this page]&lt;br /&gt;
if you're running that Archivematica version or higher.&lt;br /&gt;
&lt;br /&gt;
=Programmatic Access to indexed AIP data=&lt;br /&gt;
&lt;br /&gt;
To access indexed AIP data using a custom script or application, find an&lt;br /&gt;
Elasticsearch API (Application Programming Interface) library for the&lt;br /&gt;
programming language you are most comfortable with. In Archivematica we use&lt;br /&gt;
Python with the Elasticsearch supported&lt;br /&gt;
[https://github.com/elastic/elasticsearch-py library]. In our developer&lt;br /&gt;
documentation, we will demonstrate how to use this and Python to access AIP&lt;br /&gt;
data, but any programming language, such as PHP and&lt;br /&gt;
[https://github.com/ruflin/Elastica/ Elastica], should work.&lt;br /&gt;
&lt;br /&gt;
A list of [https://www.elastic.co/guide/en/elasticsearch/client/index.html officially]&lt;br /&gt;
supported and&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/client/community/current/index.html community]&lt;br /&gt;
supported libraries can be found on the Elasticsearch website.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Elasticsearch (Archivematica 1.3+)==&lt;br /&gt;
&lt;br /&gt;
The following example will demonstrate access to the indexes using&lt;br /&gt;
Elasticsearch's own&lt;br /&gt;
[https://github.com/elastic/elasticsearch-py Python library].&lt;br /&gt;
&lt;br /&gt;
===Importing the Elasticsearch API module===&lt;br /&gt;
&lt;br /&gt;
The first step is to import the Elasticsearch module and connect to the&lt;br /&gt;
Elasticsearch server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch(['127.0.0.1:9200'])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''NB.''' The additional module imports are used in this example so that the&lt;br /&gt;
examples below can be copied-and-pasted as desired.&lt;br /&gt;
&lt;br /&gt;
===Full text searching===&lt;br /&gt;
&lt;br /&gt;
Once connected to Elasticsearch, you can perform searches. Below is the code&lt;br /&gt;
needed to do a wildcard ('*') search for all indexed AIP files. We retrieve the&lt;br /&gt;
first 20 items. Instead of providing a wildcard you could also supply keywords,&lt;br /&gt;
such as a specific AIP UUID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: string query, https://git.io/vhgUw&lt;br /&gt;
wildcard_query =   { &amp;quot;query&amp;quot;: {&lt;br /&gt;
   &amp;quot;query_string&amp;quot; : {&lt;br /&gt;
   &amp;quot;query&amp;quot;: &amp;quot;*&amp;quot;,&lt;br /&gt;
   },&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
   results = conn.search(&lt;br /&gt;
      body=wildcard_query,&lt;br /&gt;
      index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
      doc_type=&amp;quot;aip&amp;quot;,&lt;br /&gt;
      from_=start_page - 1,&lt;br /&gt;
      size=items_per_page,&lt;br /&gt;
   )&lt;br /&gt;
except RequestError:&lt;br /&gt;
   print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
   sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
   print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
   sys.exit()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are a number of ways to construct Elasticsearch queries. The&lt;br /&gt;
Elasticsearch website provides useful reference material:&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html Elasticsearch Full Text Queries].&lt;br /&gt;
&lt;br /&gt;
===Querying for specific data===&lt;br /&gt;
&lt;br /&gt;
While the string query-type is good for broad searches, you may want to&lt;br /&gt;
narrow a search down to a specific field of data to reduce false positives.&lt;br /&gt;
Below is an example of searching documents, using a &amp;quot;term&amp;quot; query to match&lt;br /&gt;
criteria within specific data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: term query, https://git.io/vhrI9&lt;br /&gt;
term_query = {&lt;br /&gt;
    &amp;quot;query&amp;quot;: {&lt;br /&gt;
        &amp;quot;constant_score&amp;quot;: {&lt;br /&gt;
            &amp;quot;filter&amp;quot;: {&lt;br /&gt;
                &amp;quot;term&amp;quot;: {&lt;br /&gt;
                    &amp;quot;mets.ns0:mets_dict_list.ns0:amdSec_dict_list.@ID&amp;quot;:&lt;br /&gt;
                    &amp;quot;amdsec_8&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=term_query,&lt;br /&gt;
        index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;aip&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the construction of the query is not straightforward. Fields and&lt;br /&gt;
values are stored in Elasticsearch in lowercase. Properties work in uppercase&lt;br /&gt;
and might not work in lowercase. You can analyze a query with a `curl`&lt;br /&gt;
statement along the lines of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
curl -X GET &amp;quot;http://127.0.0.1:9200/_analyze?pretty=true&amp;quot; \&lt;br /&gt;
-H 'Content-Type: application/json' -d'&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;field&amp;quot;: &amp;quot;METS.ns0:mets_dict_list.ns0:amdSec_dict_list.@ID&amp;quot;,&lt;br /&gt;
  &amp;quot;text&amp;quot;: &amp;quot;amdSec_8&amp;quot;&lt;br /&gt;
}'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result is an analysis of the query string we want to use and it can reveal&lt;br /&gt;
common mistakes in our intuition, for example, searching for `amdSec_8` as a&lt;br /&gt;
mixed case string.&lt;br /&gt;
&lt;br /&gt;
===Displaying search results===&lt;br /&gt;
&lt;br /&gt;
Now that you have performed a couple of searches, you can display some results.&lt;br /&gt;
The logic below cycles through each hit in a results set. For each AIP file,&lt;br /&gt;
the UUID and filepath of the AIP are printed to the console.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for r_ in res.items():&lt;br /&gt;
        if r_[0] == &amp;quot;total&amp;quot;:&lt;br /&gt;
            print(&amp;quot;Total results:&amp;quot;, r_[1])&lt;br /&gt;
        if r_[0] == &amp;quot;hits&amp;quot;:&lt;br /&gt;
            print(&amp;quot;Results returned:&amp;quot;, len(r_[1]))&lt;br /&gt;
            for aip_index in r_[1]:&lt;br /&gt;
                # aip_index will be the complete AIP record as a Python dict&lt;br /&gt;
                if aip_index.get(&amp;quot;_source&amp;quot;):&lt;br /&gt;
                    print(&lt;br /&gt;
                        &amp;quot;AIP ID: {}&amp;quot;.format(&lt;br /&gt;
                            aip_index.get(&amp;quot;_source&amp;quot;).get(&amp;quot;filePath&amp;quot;)&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
                    print(&lt;br /&gt;
                        &amp;quot;Filepath: {}&amp;quot;.format(&lt;br /&gt;
                            aip_index.get(&amp;quot;_source&amp;quot;).get(&amp;quot;uuid&amp;quot;)&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fetching specific documents===&lt;br /&gt;
&lt;br /&gt;
The AIP index inside Archivematica is separated into two Elasticsearch document&lt;br /&gt;
types. The AIP as a whole, and its individual files.&lt;br /&gt;
&lt;br /&gt;
* ''aip''&lt;br /&gt;
* ''aipfile''&lt;br /&gt;
&lt;br /&gt;
It might be easier to retrieve information about a specific digital object if&lt;br /&gt;
you know its UUID and want to query the ```aipfile``` instead. The example&lt;br /&gt;
below shows how we might retrieve the format identification for the file with&lt;br /&gt;
the UUID: ''f7428196-a11b-4093-b311-d43d607d54ca''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch([&amp;quot;http://127.0.0.1:9200&amp;quot;])&lt;br /&gt;
&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
term_query = {&lt;br /&gt;
    &amp;quot;query&amp;quot;: {&lt;br /&gt;
        &amp;quot;constant_score&amp;quot;: {&lt;br /&gt;
            &amp;quot;filter&amp;quot;: {&lt;br /&gt;
                &amp;quot;term&amp;quot;: {&amp;quot;FILEUUID&amp;quot;: &amp;quot;f7428196-a11b-4093-b311-d43d607d54ca&amp;quot;}&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=term_query,&lt;br /&gt;
        index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;aipfile&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for res_ in res.get(&amp;quot;hits&amp;quot;):&lt;br /&gt;
        file_record = res_.get(&amp;quot;_source&amp;quot;)&lt;br /&gt;
        if file_record:&lt;br /&gt;
            try:&lt;br /&gt;
                puid = file_record[&amp;quot;METS&amp;quot;][&amp;quot;amdSec&amp;quot;] \&lt;br /&gt;
                [&amp;quot;ns0:amdSec_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:techMD_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:mdWrap_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:xmlData_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:object_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns1:objectCharacteristics_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:format_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns1:formatRegistry_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:formatRegistryKey&amp;quot;]&lt;br /&gt;
                print(&amp;quot;Format ID: {}&amp;quot;.format(puid))&lt;br /&gt;
            except KeyError:&lt;br /&gt;
                print(&amp;quot;Problem accessing index.&amp;quot;)&lt;br /&gt;
                sys.exit(1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Archivematica Transfer Index===&lt;br /&gt;
&lt;br /&gt;
Information about Transfers is also indexed in Archivematica. The information&lt;br /&gt;
is less-rich when compared to what is stored in the AIP and so it is not&lt;br /&gt;
covered in detail here. We can use the wildcard query from the AIP examples&lt;br /&gt;
above to begin to look at what is in the transfer index. The query would look&lt;br /&gt;
like as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch([&amp;quot;http://127.0.0.1:62002&amp;quot;])&lt;br /&gt;
&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: string query, https://git.io/vhgUw&lt;br /&gt;
wildcard_query =   { &amp;quot;query&amp;quot;: {&lt;br /&gt;
   &amp;quot;query_string&amp;quot; : {&lt;br /&gt;
   &amp;quot;query&amp;quot;: &amp;quot;*&amp;quot;,&lt;br /&gt;
   },&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=wildcard_query,&lt;br /&gt;
        index=&amp;quot;transfers&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;transfer&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for res_ in res.get(&amp;quot;hits&amp;quot;):&lt;br /&gt;
        print(res_.get(&amp;quot;_source&amp;quot;))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Further reading===&lt;br /&gt;
&lt;br /&gt;
Elasticsearch provides API functions beyond&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html searching].&lt;br /&gt;
Users who wish to make use of these capabilities in Python&lt;br /&gt;
can look at the Python [https://elasticsearch-py.readthedocs.io/en/master/ library documentation].&lt;br /&gt;
&lt;br /&gt;
The complete [https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html Elasticsearch documentation]&lt;br /&gt;
reference is also available. Many of the commands are described with examples&lt;br /&gt;
that can be run using the [https://curl.haxx.se/ curl] command line tool.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Elasticsearch (Archivematica 0.9 up to Archivematica 1.2)==&lt;br /&gt;
&lt;br /&gt;
Here we will run through an example of interfacing with older versions of&lt;br /&gt;
Archivematica using Elasticsearch with a Python script that leverages the pyes&lt;br /&gt;
library.&lt;br /&gt;
&lt;br /&gt;
'''NB.''' Pyes use was [https://github.com/artefactual/archivematica/commit/b0ac6c642a2f070fc7a0f7c198a51c2d0509b7f7 removed]&lt;br /&gt;
in Archivematica 1.3. Though with some modification to the examples below it&lt;br /&gt;
should still be possible to adopt it to query the ES indexes.&lt;br /&gt;
&lt;br /&gt;
===Importing the pyes module===&lt;br /&gt;
&lt;br /&gt;
The first step, when using pyes, is to require the module. The following code&lt;br /&gt;
imports pyes functionality on a system on which Archivematica is installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/home/demo/archivematica/src/archivematicaCommon/lib/externals&amp;quot;)&lt;br /&gt;
from pyes import *&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next you'll want to create a connection to Elasticsearch.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
conn = ES('127.0.0.1:9200')&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Full text searching===&lt;br /&gt;
&lt;br /&gt;
Once connected to Elasticsearch, you can perform searches. Below is the code&lt;br /&gt;
needed to do a &amp;quot;wildcard&amp;quot; search for all AIP files indexed by Elasticsearch and&lt;br /&gt;
retrieve the first 20 items. Instead of doing a &amp;quot;wildcard&amp;quot; search you could&lt;br /&gt;
also supply keywords, such as a certain AIP UUID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page     = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
q = StringQuery('*')&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search_raw(&lt;br /&gt;
        query=q,&lt;br /&gt;
        indices='aips',&lt;br /&gt;
        type='aip',&lt;br /&gt;
        start=start_page - 1,&lt;br /&gt;
        size=items_per_page&lt;br /&gt;
     )&lt;br /&gt;
except:&lt;br /&gt;
    print 'Query error.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Querying for specific data===&lt;br /&gt;
&lt;br /&gt;
While the &amp;quot;StringQuery&amp;quot; query type is good for broad searches, you may want to&lt;br /&gt;
narrow a search down to a specific field of data to reduce false-positives.&lt;br /&gt;
Below is an example of searching documents, using &amp;quot;TermQuery&amp;quot;, matching&lt;br /&gt;
criteria within specific data. As, by default, Elasticsearch stores term values&lt;br /&gt;
in lowercase the term value searched for must also be lowercase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/usr/lib/archivematica/archivematicaCommon/externals&amp;quot;)&lt;br /&gt;
import pyes&lt;br /&gt;
&lt;br /&gt;
conn = pyes.ES('127.0.0.1:9200')&lt;br /&gt;
&lt;br /&gt;
q = pyes.TermQuery(&amp;quot;METS.amdSec.ns0:amdSec_list.@ID&amp;quot;, &amp;quot;amdsec_8&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search_raw(query=q, indices='aips')&lt;br /&gt;
except:&lt;br /&gt;
  print 'Query failed.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Displaying search results===&lt;br /&gt;
&lt;br /&gt;
Now that you've performed a couple of searches, you can display some results.&lt;br /&gt;
The below logic cycles through each hit in a results set, representing an AIP&lt;br /&gt;
file, and prints the UUID of the AIP the file belongs in, the Elasticsearch&lt;br /&gt;
document ID corresponding to the indexed file data, and the path of the file&lt;br /&gt;
within the AIP.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if results:&lt;br /&gt;
    document_ids = []&lt;br /&gt;
    for item in results.hits.hits:&lt;br /&gt;
        aip = item._source&lt;br /&gt;
        print 'AIP ID: ' + aip['AIPUUID'] + ' / Document ID: ' + item._id&lt;br /&gt;
        print 'Filepath: ' + aip['filePath']&lt;br /&gt;
        print&lt;br /&gt;
        document_ids.append(item._id)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fetching specific documents===&lt;br /&gt;
&lt;br /&gt;
If you want to get Elasticsearch data for a specific AIP file, you can use the&lt;br /&gt;
Elasticsearch document ID. The above code populates the&lt;br /&gt;
&amp;lt;code&amp;gt;document_ids&amp;lt;/code&amp;gt; array and the below code uses this data, retrieving&lt;br /&gt;
individual documents and extracting a specific item of data from each document.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for document_id in document_ids:&lt;br /&gt;
    data = conn.get(index_name, type_name, document_id)&lt;br /&gt;
&lt;br /&gt;
    format = data['METS']['amdSec'] \&lt;br /&gt;
    ['ns0:amdSec_list'][0]  \&lt;br /&gt;
    ['ns0:techMD_list'][0]  \&lt;br /&gt;
    ['ns0:mdWrap_list'][0]  \&lt;br /&gt;
    ['ns0:xmlData_list'][0] \&lt;br /&gt;
    ['ns1:object_list'][0]  \&lt;br /&gt;
    ['ns1:objectCharacteristics_list'][0] \&lt;br /&gt;
    ['ns1:format_list'][0]  \&lt;br /&gt;
    ['ns1:formatDesignation_list'][0] \&lt;br /&gt;
    ['ns1:formatName']&lt;br /&gt;
&lt;br /&gt;
    print 'Format for document ID ' + document_id + ' is ' + format&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Augmenting documents===&lt;br /&gt;
&lt;br /&gt;
To add additional data to an Elasticsearch document, you will need the document&lt;br /&gt;
ID. The following code shows an Elasticsearch query being used to find a&lt;br /&gt;
document and update it with additional data. Note that the name of the data&lt;br /&gt;
field being added, &amp;quot;__public&amp;quot;, is prefixed with two underscores. This practice&lt;br /&gt;
prevents the accidental overwriting of system or Archivematica-specific data.&lt;br /&gt;
System data is prefixed with a single underscore.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/usr/lib/archivematica/archivematicaCommon/externals&amp;quot;)&lt;br /&gt;
import pyes&lt;br /&gt;
&lt;br /&gt;
conn = pyes.ES('127.0.0.1:9200')&lt;br /&gt;
&lt;br /&gt;
q = pyes.TermQuery(&amp;quot;METS.amdSec.ns0:amdSec_list.@ID&amp;quot;, &amp;quot;amdsec_8&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
results = conn.search_raw(query=q, indices='aips')&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
  if results:&lt;br /&gt;
    for item in results.hits.hits:&lt;br /&gt;
        print 'Updating ID: ' + item['_id']&lt;br /&gt;
&lt;br /&gt;
        document = item['_source']&lt;br /&gt;
        document['__public'] = 'yes'&lt;br /&gt;
        conn.index(document, 'aips', 'aip', item['_id'])&lt;br /&gt;
except:&lt;br /&gt;
  print 'Query failed.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jraddaoui</name></author>
	</entry>
	<entry>
		<id>https://wiki.archivematica.org/index.php?title=Elasticsearch_Development&amp;diff=12874</id>
		<title>Elasticsearch Development</title>
		<link rel="alternate" type="text/html" href="https://wiki.archivematica.org/index.php?title=Elasticsearch_Development&amp;diff=12874"/>
		<updated>2019-02-15T17:24:04Z</updated>

		<summary type="html">&lt;p&gt;Jraddaoui: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;From Archivematica 0.9, AIP package information, such as METS data, is indexed&lt;br /&gt;
using [http://www.elasticsearch.org/ Elasticsearch (ES)]. This data can be&lt;br /&gt;
searched from the Archival Storage area of the dashboard or can be interfaced&lt;br /&gt;
with programmatically. For Elasticsearch administration information, such as&lt;br /&gt;
how to delete an Elasticsearch index, please reference the&lt;br /&gt;
[[Administrator_manual_1.2#Elasticsearch|administrator manual]].&lt;br /&gt;
&lt;br /&gt;
'''NB.''' From Archivematica 1.7.0, users are given the option of whether to&lt;br /&gt;
index information using Elasticsearch, and so the information below might not&lt;br /&gt;
work. It will be dependant on how your Archivematica instance has been&lt;br /&gt;
configured.&lt;br /&gt;
&lt;br /&gt;
'''NB.''' In Archivematica 1.9.0, the Elasticsearch version support has been&lt;br /&gt;
upgraded from ES 1.x to the 6.x version. Check [https://wiki.archivematica.org/Elasticsearch_Development_1.9 this page]&lt;br /&gt;
 if you're running that Archivematica version or higher.&lt;br /&gt;
&lt;br /&gt;
=Programmatic Access to indexed AIP data=&lt;br /&gt;
&lt;br /&gt;
To access indexed AIP data using a custom script or application, find an&lt;br /&gt;
Elasticsearch API (Application Programming Interface) library for the&lt;br /&gt;
programming language you are most comfortable with. In Archivematica we use&lt;br /&gt;
Python with the Elasticsearch supported&lt;br /&gt;
[https://github.com/elastic/elasticsearch-py library]. In our developer&lt;br /&gt;
documentation, we will demonstrate how to use this and Python to access AIP&lt;br /&gt;
data, but any programming language, such as PHP and&lt;br /&gt;
[https://github.com/ruflin/Elastica/ Elastica], should work.&lt;br /&gt;
&lt;br /&gt;
A list of [https://www.elastic.co/guide/en/elasticsearch/client/index.html officially]&lt;br /&gt;
supported and&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/client/community/current/index.html community]&lt;br /&gt;
supported libraries can be found on the Elasticsearch website.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Elasticsearch (Archivematica 1.3+)==&lt;br /&gt;
&lt;br /&gt;
The following example will demonstrate access to the indexes using&lt;br /&gt;
Elasticsearch's own&lt;br /&gt;
[https://github.com/elastic/elasticsearch-py Python library].&lt;br /&gt;
&lt;br /&gt;
===Importing the Elasticsearch API module===&lt;br /&gt;
&lt;br /&gt;
The first step is to import the Elasticsearch module and connect to the&lt;br /&gt;
Elasticsearch server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch(['127.0.0.1:9200'])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''NB.''' The additional module imports are used in this example so that the&lt;br /&gt;
examples below can be copied-and-pasted as desired.&lt;br /&gt;
&lt;br /&gt;
===Full text searching===&lt;br /&gt;
&lt;br /&gt;
Once connected to Elasticsearch, you can perform searches. Below is the code&lt;br /&gt;
needed to do a wildcard ('*') search for all indexed AIP files. We retrieve the&lt;br /&gt;
first 20 items. Instead of providing a wildcard you could also supply keywords,&lt;br /&gt;
such as a specific AIP UUID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: string query, https://git.io/vhgUw&lt;br /&gt;
wildcard_query =   { &amp;quot;query&amp;quot;: {&lt;br /&gt;
   &amp;quot;query_string&amp;quot; : {&lt;br /&gt;
   &amp;quot;query&amp;quot;: &amp;quot;*&amp;quot;,&lt;br /&gt;
   },&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
   results = conn.search(&lt;br /&gt;
      body=wildcard_query,&lt;br /&gt;
      index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
      doc_type=&amp;quot;aip&amp;quot;,&lt;br /&gt;
      from_=start_page - 1,&lt;br /&gt;
      size=items_per_page,&lt;br /&gt;
   )&lt;br /&gt;
except RequestError:&lt;br /&gt;
   print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
   sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
   print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
   sys.exit()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are a number of ways to construct Elasticsearch queries. The&lt;br /&gt;
Elasticsearch website provides useful reference material:&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html Elasticsearch Full Text Queries].&lt;br /&gt;
&lt;br /&gt;
===Querying for specific data===&lt;br /&gt;
&lt;br /&gt;
While the string query-type is good for broad searches, you may want to&lt;br /&gt;
narrow a search down to a specific field of data to reduce false positives.&lt;br /&gt;
Below is an example of searching documents, using a &amp;quot;term&amp;quot; query to match&lt;br /&gt;
criteria within specific data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: term query, https://git.io/vhrI9&lt;br /&gt;
term_query = {&lt;br /&gt;
    &amp;quot;query&amp;quot;: {&lt;br /&gt;
        &amp;quot;constant_score&amp;quot;: {&lt;br /&gt;
            &amp;quot;filter&amp;quot;: {&lt;br /&gt;
                &amp;quot;term&amp;quot;: {&lt;br /&gt;
                    &amp;quot;mets.ns0:mets_dict_list.ns0:amdSec_dict_list.@ID&amp;quot;:&lt;br /&gt;
                    &amp;quot;amdsec_8&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=term_query,&lt;br /&gt;
        index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;aip&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the construction of the query is not straightforward. Fields and&lt;br /&gt;
values are stored in Elasticsearch in lowercase. Properties work in uppercase&lt;br /&gt;
and might not work in lowercase. You can analyze a query with a `curl`&lt;br /&gt;
statement along the lines of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
curl -X GET &amp;quot;http://127.0.0.1:9200/_analyze?pretty=true&amp;quot; \&lt;br /&gt;
-H 'Content-Type: application/json' -d'&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;field&amp;quot;: &amp;quot;METS.ns0:mets_dict_list.ns0:amdSec_dict_list.@ID&amp;quot;,&lt;br /&gt;
  &amp;quot;text&amp;quot;: &amp;quot;amdSec_8&amp;quot;&lt;br /&gt;
}'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result is an analysis of the query string we want to use and it can reveal&lt;br /&gt;
common mistakes in our intuition, for example, searching for `amdSec_8` as a&lt;br /&gt;
mixed case string.&lt;br /&gt;
&lt;br /&gt;
===Displaying search results===&lt;br /&gt;
&lt;br /&gt;
Now that you have performed a couple of searches, you can display some results.&lt;br /&gt;
The logic below cycles through each hit in a results set. For each AIP file,&lt;br /&gt;
the UUID and filepath of the AIP are printed to the console.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for r_ in res.items():&lt;br /&gt;
        if r_[0] == &amp;quot;total&amp;quot;:&lt;br /&gt;
            print(&amp;quot;Total results:&amp;quot;, r_[1])&lt;br /&gt;
        if r_[0] == &amp;quot;hits&amp;quot;:&lt;br /&gt;
            print(&amp;quot;Results returned:&amp;quot;, len(r_[1]))&lt;br /&gt;
            for aip_index in r_[1]:&lt;br /&gt;
                # aip_index will be the complete AIP record as a Python dict&lt;br /&gt;
                if aip_index.get(&amp;quot;_source&amp;quot;):&lt;br /&gt;
                    print(&lt;br /&gt;
                        &amp;quot;AIP ID: {}&amp;quot;.format(&lt;br /&gt;
                            aip_index.get(&amp;quot;_source&amp;quot;).get(&amp;quot;filePath&amp;quot;)&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
                    print(&lt;br /&gt;
                        &amp;quot;Filepath: {}&amp;quot;.format(&lt;br /&gt;
                            aip_index.get(&amp;quot;_source&amp;quot;).get(&amp;quot;uuid&amp;quot;)&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fetching specific documents===&lt;br /&gt;
&lt;br /&gt;
The AIP index inside Archivematica is separated into two Elasticsearch document&lt;br /&gt;
types. The AIP as a whole, and its individual files.&lt;br /&gt;
&lt;br /&gt;
* ''aip''&lt;br /&gt;
* ''aipfile''&lt;br /&gt;
&lt;br /&gt;
It might be easier to retrieve information about a specific digital object if&lt;br /&gt;
you know its UUID and want to query the ```aipfile``` instead. The example&lt;br /&gt;
below shows how we might retrieve the format identification for the file with&lt;br /&gt;
the UUID: ''f7428196-a11b-4093-b311-d43d607d54ca''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch([&amp;quot;http://127.0.0.1:9200&amp;quot;])&lt;br /&gt;
&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
term_query = {&lt;br /&gt;
    &amp;quot;query&amp;quot;: {&lt;br /&gt;
        &amp;quot;constant_score&amp;quot;: {&lt;br /&gt;
            &amp;quot;filter&amp;quot;: {&lt;br /&gt;
                &amp;quot;term&amp;quot;: {&amp;quot;FILEUUID&amp;quot;: &amp;quot;f7428196-a11b-4093-b311-d43d607d54ca&amp;quot;}&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=term_query,&lt;br /&gt;
        index=&amp;quot;aips&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;aipfile&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for res_ in res.get(&amp;quot;hits&amp;quot;):&lt;br /&gt;
        file_record = res_.get(&amp;quot;_source&amp;quot;)&lt;br /&gt;
        if file_record:&lt;br /&gt;
            try:&lt;br /&gt;
                puid = file_record[&amp;quot;METS&amp;quot;][&amp;quot;amdSec&amp;quot;] \&lt;br /&gt;
                [&amp;quot;ns0:amdSec_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:techMD_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:mdWrap_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns0:xmlData_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:object_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns1:objectCharacteristics_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:format_dict_list&amp;quot;][0]  \&lt;br /&gt;
                [&amp;quot;ns1:formatRegistry_dict_list&amp;quot;][0] \&lt;br /&gt;
                [&amp;quot;ns1:formatRegistryKey&amp;quot;]&lt;br /&gt;
                print(&amp;quot;Format ID: {}&amp;quot;.format(puid))&lt;br /&gt;
            except KeyError:&lt;br /&gt;
                print(&amp;quot;Problem accessing index.&amp;quot;)&lt;br /&gt;
                sys.exit(1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Archivematica Transfer Index===&lt;br /&gt;
&lt;br /&gt;
Information about Transfers is also indexed in Archivematica. The information&lt;br /&gt;
is less-rich when compared to what is stored in the AIP and so it is not&lt;br /&gt;
covered in detail here. We can use the wildcard query from the AIP examples&lt;br /&gt;
above to begin to look at what is in the transfer index. The query would look&lt;br /&gt;
like as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from __future__ import print_function&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from elasticsearch import Elasticsearch&lt;br /&gt;
from elasticsearch.exceptions import RequestError, NotFoundError&lt;br /&gt;
&lt;br /&gt;
conn = Elasticsearch([&amp;quot;http://127.0.0.1:62002&amp;quot;])&lt;br /&gt;
&lt;br /&gt;
start_page = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
# ref: string query, https://git.io/vhgUw&lt;br /&gt;
wildcard_query =   { &amp;quot;query&amp;quot;: {&lt;br /&gt;
   &amp;quot;query_string&amp;quot; : {&lt;br /&gt;
   &amp;quot;query&amp;quot;: &amp;quot;*&amp;quot;,&lt;br /&gt;
   },&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search(&lt;br /&gt;
        body=wildcard_query,&lt;br /&gt;
        index=&amp;quot;transfers&amp;quot;,&lt;br /&gt;
        doc_type=&amp;quot;transfer&amp;quot;,&lt;br /&gt;
        from_=start_page - 1,&lt;br /&gt;
        size=items_per_page,&lt;br /&gt;
    )&lt;br /&gt;
except RequestError:&lt;br /&gt;
    print(&amp;quot;Query error&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
except NotFoundError:&lt;br /&gt;
    print(&amp;quot;No results found&amp;quot;)&lt;br /&gt;
    sys.exit()&lt;br /&gt;
&lt;br /&gt;
res = results.get(&amp;quot;hits&amp;quot;)&lt;br /&gt;
if res is not None:&lt;br /&gt;
    for res_ in res.get(&amp;quot;hits&amp;quot;):&lt;br /&gt;
        print(res_.get(&amp;quot;_source&amp;quot;))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Further reading===&lt;br /&gt;
&lt;br /&gt;
Elasticsearch provides API functions beyond&lt;br /&gt;
[https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html searching].&lt;br /&gt;
Users who wish to make use of these capabilities in Python&lt;br /&gt;
can look at the Python [https://elasticsearch-py.readthedocs.io/en/master/ library documentation].&lt;br /&gt;
&lt;br /&gt;
The complete [https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html Elasticsearch documentation]&lt;br /&gt;
reference is also available. Many of the commands are described with examples&lt;br /&gt;
that can be run using the [https://curl.haxx.se/ curl] command line tool.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Elasticsearch (Archivematica 0.9 up to Archivematica 1.2)==&lt;br /&gt;
&lt;br /&gt;
Here we will run through an example of interfacing with older versions of&lt;br /&gt;
Archivematica using Elasticsearch with a Python script that leverages the pyes&lt;br /&gt;
library.&lt;br /&gt;
&lt;br /&gt;
'''NB.''' Pyes use was [https://github.com/artefactual/archivematica/commit/b0ac6c642a2f070fc7a0f7c198a51c2d0509b7f7 removed]&lt;br /&gt;
in Archivematica 1.3. Though with some modification to the examples below it&lt;br /&gt;
should still be possible to adopt it to query the ES indexes.&lt;br /&gt;
&lt;br /&gt;
===Importing the pyes module===&lt;br /&gt;
&lt;br /&gt;
The first step, when using pyes, is to require the module. The following code&lt;br /&gt;
imports pyes functionality on a system on which Archivematica is installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/home/demo/archivematica/src/archivematicaCommon/lib/externals&amp;quot;)&lt;br /&gt;
from pyes import *&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next you'll want to create a connection to Elasticsearch.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
conn = ES('127.0.0.1:9200')&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Full text searching===&lt;br /&gt;
&lt;br /&gt;
Once connected to Elasticsearch, you can perform searches. Below is the code&lt;br /&gt;
needed to do a &amp;quot;wildcard&amp;quot; search for all AIP files indexed by Elasticsearch and&lt;br /&gt;
retrieve the first 20 items. Instead of doing a &amp;quot;wildcard&amp;quot; search you could&lt;br /&gt;
also supply keywords, such as a certain AIP UUID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start_page     = 1&lt;br /&gt;
items_per_page = 20&lt;br /&gt;
&lt;br /&gt;
q = StringQuery('*')&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search_raw(&lt;br /&gt;
        query=q,&lt;br /&gt;
        indices='aips',&lt;br /&gt;
        type='aip',&lt;br /&gt;
        start=start_page - 1,&lt;br /&gt;
        size=items_per_page&lt;br /&gt;
     )&lt;br /&gt;
except:&lt;br /&gt;
    print 'Query error.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Querying for specific data===&lt;br /&gt;
&lt;br /&gt;
While the &amp;quot;StringQuery&amp;quot; query type is good for broad searches, you may want to&lt;br /&gt;
narrow a search down to a specific field of data to reduce false-positives.&lt;br /&gt;
Below is an example of searching documents, using &amp;quot;TermQuery&amp;quot;, matching&lt;br /&gt;
criteria within specific data. As, by default, Elasticsearch stores term values&lt;br /&gt;
in lowercase the term value searched for must also be lowercase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/usr/lib/archivematica/archivematicaCommon/externals&amp;quot;)&lt;br /&gt;
import pyes&lt;br /&gt;
&lt;br /&gt;
conn = pyes.ES('127.0.0.1:9200')&lt;br /&gt;
&lt;br /&gt;
q = pyes.TermQuery(&amp;quot;METS.amdSec.ns0:amdSec_list.@ID&amp;quot;, &amp;quot;amdsec_8&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    results = conn.search_raw(query=q, indices='aips')&lt;br /&gt;
except:&lt;br /&gt;
  print 'Query failed.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Displaying search results===&lt;br /&gt;
&lt;br /&gt;
Now that you've performed a couple of searches, you can display some results.&lt;br /&gt;
The below logic cycles through each hit in a results set, representing an AIP&lt;br /&gt;
file, and prints the UUID of the AIP the file belongs in, the Elasticsearch&lt;br /&gt;
document ID corresponding to the indexed file data, and the path of the file&lt;br /&gt;
within the AIP.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if results:&lt;br /&gt;
    document_ids = []&lt;br /&gt;
    for item in results.hits.hits:&lt;br /&gt;
        aip = item._source&lt;br /&gt;
        print 'AIP ID: ' + aip['AIPUUID'] + ' / Document ID: ' + item._id&lt;br /&gt;
        print 'Filepath: ' + aip['filePath']&lt;br /&gt;
        print&lt;br /&gt;
        document_ids.append(item._id)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fetching specific documents===&lt;br /&gt;
&lt;br /&gt;
If you want to get Elasticsearch data for a specific AIP file, you can use the&lt;br /&gt;
Elasticsearch document ID. The above code populates the&lt;br /&gt;
&amp;lt;code&amp;gt;document_ids&amp;lt;/code&amp;gt; array and the below code uses this data, retrieving&lt;br /&gt;
individual documents and extracting a specific item of data from each document.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for document_id in document_ids:&lt;br /&gt;
    data = conn.get(index_name, type_name, document_id)&lt;br /&gt;
&lt;br /&gt;
    format = data['METS']['amdSec'] \&lt;br /&gt;
    ['ns0:amdSec_list'][0]  \&lt;br /&gt;
    ['ns0:techMD_list'][0]  \&lt;br /&gt;
    ['ns0:mdWrap_list'][0]  \&lt;br /&gt;
    ['ns0:xmlData_list'][0] \&lt;br /&gt;
    ['ns1:object_list'][0]  \&lt;br /&gt;
    ['ns1:objectCharacteristics_list'][0] \&lt;br /&gt;
    ['ns1:format_list'][0]  \&lt;br /&gt;
    ['ns1:formatDesignation_list'][0] \&lt;br /&gt;
    ['ns1:formatName']&lt;br /&gt;
&lt;br /&gt;
    print 'Format for document ID ' + document_id + ' is ' + format&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Augmenting documents===&lt;br /&gt;
&lt;br /&gt;
To add additional data to an Elasticsearch document, you will need the document&lt;br /&gt;
ID. The following code shows an Elasticsearch query being used to find a&lt;br /&gt;
document and update it with additional data. Note that the name of the data&lt;br /&gt;
field being added, &amp;quot;__public&amp;quot;, is prefixed with two underscores. This practice&lt;br /&gt;
prevents the accidental overwriting of system or Archivematica-specific data.&lt;br /&gt;
System data is prefixed with a single underscore.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
sys.path.append(&amp;quot;/usr/lib/archivematica/archivematicaCommon/externals&amp;quot;)&lt;br /&gt;
import pyes&lt;br /&gt;
&lt;br /&gt;
conn = pyes.ES('127.0.0.1:9200')&lt;br /&gt;
&lt;br /&gt;
q = pyes.TermQuery(&amp;quot;METS.amdSec.ns0:amdSec_list.@ID&amp;quot;, &amp;quot;amdsec_8&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
results = conn.search_raw(query=q, indices='aips')&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
  if results:&lt;br /&gt;
    for item in results.hits.hits:&lt;br /&gt;
        print 'Updating ID: ' + item['_id']&lt;br /&gt;
&lt;br /&gt;
        document = item['_source']&lt;br /&gt;
        document['__public'] = 'yes'&lt;br /&gt;
        conn.index(document, 'aips', 'aip', item['_id'])&lt;br /&gt;
except:&lt;br /&gt;
  print 'Query failed.'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jraddaoui</name></author>
	</entry>
	<entry>
		<id>https://wiki.archivematica.org/index.php?title=Elasticsearch_Development_1.9&amp;diff=12873</id>
		<title>Elasticsearch Development 1.9</title>
		<link rel="alternate" type="text/html" href="https://wiki.archivematica.org/index.php?title=Elasticsearch_Development_1.9&amp;diff=12873"/>
		<updated>2019-02-15T17:22:58Z</updated>

		<summary type="html">&lt;p&gt;Jraddaoui: Created page with &amp;quot;Work in progress.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Work in progress.&lt;/div&gt;</summary>
		<author><name>Jraddaoui</name></author>
	</entry>
	<entry>
		<id>https://wiki.archivematica.org/index.php?title=Storage_Service_API&amp;diff=12187</id>
		<title>Storage Service API</title>
		<link rel="alternate" type="text/html" href="https://wiki.archivematica.org/index.php?title=Storage_Service_API&amp;diff=12187"/>
		<updated>2017-12-29T01:42:20Z</updated>

		<summary type="html">&lt;p&gt;Jraddaoui: Add browsing notes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Main Page]] &amp;gt; [[Development]] &amp;gt; Storage Service API&lt;br /&gt;
&lt;br /&gt;
The [[Storage Service]] API provides programmatic access to moving files around in storage areas that the Storage Service has access to.&lt;br /&gt;
&lt;br /&gt;
The API is written using [http://django-tastypie.readthedocs.io/en/latest/ TastyPie].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: TastyPie is less well supported than [http://www.django-rest-framework.org/ Django REST Framework], both in terms of docs &amp;amp; community. We should look at replacing TastyPie with DRF.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Endpoints require authentication with a username and API key.  This can be submitted as GET parameters (eg &amp;lt;code&amp;gt;?username=test&amp;amp;api_key=e6282adabed84e39ffe451f8bf6ff1a67c1fc9f2&amp;lt;/code&amp;gt;) or as a header (eg &amp;lt;code&amp;gt;Authorization: ApiKey test:e6282adabed84e39ffe451f8bf6ff1a67c1fc9f2&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== A note about browsing ==&lt;br /&gt;
&lt;br /&gt;
A detailed schema can be found for each of the resources by adding &amp;quot;schema&amp;quot; to the get all URL.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 $ curl -X GET -H&amp;quot;Authorization: ApiKey test:95141fc645ed97a95893f1f865d24687f89a27ad&amp;quot; 'http://localhost:8000/api/v2/location/schema/?format=json&lt;br /&gt;
 {&lt;br /&gt;
    &amp;quot;allowed_detail_http_methods&amp;quot;: [&lt;br /&gt;
        &amp;quot;get&amp;quot;,&lt;br /&gt;
        &amp;quot;post&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;allowed_list_http_methods&amp;quot;: [&lt;br /&gt;
        &amp;quot;get&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;default_format&amp;quot;: &amp;quot;application/json&amp;quot;,&lt;br /&gt;
    &amp;quot;default_limit&amp;quot;: 20,&lt;br /&gt;
    &amp;quot;fields&amp;quot;: {&lt;br /&gt;
        &amp;quot;description&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Unicode string data. Ex: \&amp;quot;Hello World\&amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: true,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;description&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;enabled&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: true,&lt;br /&gt;
            &amp;quot;default&amp;quot;: true,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;True if space can be accessed.&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;Enabled&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;path&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Unicode string data. Ex: \&amp;quot;Hello World\&amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: true,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;path&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;pipeline&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Many related resources. Can be either a list of URIs or list of individually nested resource data.&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;related_schema&amp;quot;: &amp;quot;/api/v2/pipeline/schema/&amp;quot;,&lt;br /&gt;
            &amp;quot;related_type&amp;quot;: &amp;quot;to_many&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;related&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;pipeline&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;purpose&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Purpose of the space.  Eg. AIP storage, Transfer source&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;Purpose&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;quota&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: null,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Size, in bytes (optional)&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: true,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;Quota&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;relative_path&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Path to location, relative to the storage space's path.&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;Relative Path&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;resource_uri&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Unicode string data. Ex: \&amp;quot;Hello World\&amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: true,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;resource uri&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;space&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;No default provided.&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;A single related resource. Can be either a URI or set of nested resource data.&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;related_schema&amp;quot;: &amp;quot;/api/v2/space/schema/&amp;quot;,&lt;br /&gt;
            &amp;quot;related_type&amp;quot;: &amp;quot;to_one&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;related&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;space&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;used&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: false,&lt;br /&gt;
            &amp;quot;default&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Amount used, in bytes.&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: false,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;Used&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;uuid&amp;quot;: {&lt;br /&gt;
            &amp;quot;blank&amp;quot;: true,&lt;br /&gt;
            &amp;quot;default&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;help_text&amp;quot;: &amp;quot;Unique identifier&amp;quot;,&lt;br /&gt;
            &amp;quot;nullable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;primary_key&amp;quot;: false,&lt;br /&gt;
            &amp;quot;readonly&amp;quot;: false,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;unique&amp;quot;: true,&lt;br /&gt;
            &amp;quot;verbose_name&amp;quot;: &amp;quot;uuid&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;filtering&amp;quot;: {&lt;br /&gt;
        &amp;quot;pipeline&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;purpose&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;quota&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;relative_path&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;space&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;used&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;uuid&amp;quot;: 1&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This schema, among other things, describes the fields in the resource (including the schema URI of related resource fields) and the fields that allow filtering. Valid filtering values are: Django ORM filters (e.g. startswith, exact, lte, etc.) or 1 or 2. If a filtering field is set to 2 it can be filtered over the related resource fields. For example, the locations could be filtered by their pipeline UUID setting it in a request parameter formatted with two underscore chars: &amp;lt;code&amp;gt;/api/v2/location/?pipeline__uuid=&amp;lt;uuid&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info on how to interact with the API see:&lt;br /&gt;
&lt;br /&gt;
http://django-tastypie.readthedocs.io/en/v0.13.1/interacting.html&lt;br /&gt;
&lt;br /&gt;
== Pipeline ==&lt;br /&gt;
&lt;br /&gt;
=== Get all pipelines ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/pipeline/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;: Description of the pipeline&lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the pipeline&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;meta&amp;lt;/code&amp;gt;: Metadata on the response: number of hits, pagination information&lt;br /&gt;
** &amp;lt;code&amp;gt;objects&amp;lt;/code&amp;gt;: List of pipelines. See [[#Get pipeline details]] for format&lt;br /&gt;
&lt;br /&gt;
Returns information about all the pipelines in the system.  Can be [http://django-tastypie.readthedocs.io/en/latest/resources.html#basic-filtering filtered] by the description or uuid. Disabled pipelines are not returned.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 $ curl -X GET -H&amp;quot;Authorization: ApiKey test:95141fc645ed97a95893f1f865d24687f89a27ad&amp;quot; 'http://localhost:8000/api/v2/pipeline/?description__startswith=Archivematica' | python -m json.tool&lt;br /&gt;
 {&lt;br /&gt;
     &amp;quot;meta&amp;quot;: {&lt;br /&gt;
         &amp;quot;limit&amp;quot;: 20,&lt;br /&gt;
         &amp;quot;next&amp;quot;: null,&lt;br /&gt;
         &amp;quot;offset&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;previous&amp;quot;: null,&lt;br /&gt;
         &amp;quot;total_count&amp;quot;: 1&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;objects&amp;quot;: [&lt;br /&gt;
         {&lt;br /&gt;
             &amp;quot;description&amp;quot;: &amp;quot;Archivematica on alouette&amp;quot;,&lt;br /&gt;
             &amp;quot;remote_name&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;br /&gt;
             &amp;quot;resource_uri&amp;quot;: &amp;quot;/api/v2/pipeline/dd354557-9e6e-4918-9fe3-a65b00ecb1af/&amp;quot;,&lt;br /&gt;
             &amp;quot;uuid&amp;quot;: &amp;quot;dd354557-9e6e-4918-9fe3-a65b00ecb1af&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
     ]&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Create new pipeline ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/pipeline/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** Should contain fields for a new pipeline: &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;api_key&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;api_username&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;create_default_locations&amp;lt;/code&amp;gt;: If True, will associated default [[Storage Service#Locations | Locations]] with the newly created pipeline&lt;br /&gt;
** &amp;lt;code&amp;gt;shared_path&amp;lt;/code&amp;gt;: If default locations are created, create the [[Storage Service#Currently Processing | processing]] location at this path in the local filesystem&lt;br /&gt;
** &amp;lt;code&amp;gt;remote_name&amp;lt;/code&amp;gt;: IP or hostname of the pipeline. If not provided and  &amp;lt;code&amp;gt;create_default_locations&amp;lt;/code&amp;gt; is set, will try to populate from the IP of the request.&lt;br /&gt;
* '''Response''': JSON with data for the pipeline&lt;br /&gt;
&lt;br /&gt;
If the 'Pipelines disabled on creation' setting is set, the pipeline will be disabled by default, and will not respond to queries.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 $ curl -X POST -H&amp;quot;Authorization: ApiKey test:95141fc645ed97a95893f1f865d24687f89a27ad&amp;quot; -H&amp;quot;Content-Type: application/json&amp;quot; -d'{&amp;quot;uuid&amp;quot;: &amp;quot;99354557-9e6e-4918-9fe3-a65b00ecb199&amp;quot;, &amp;quot;description&amp;quot;: &amp;quot;Test pipeline&amp;quot;, &amp;quot;create_default_locations&amp;quot;: true, &amp;quot;api_username&amp;quot;: &amp;quot;demo&amp;quot;, &amp;quot;api_key&amp;quot;: &amp;quot;03ecb307f5b8012f4771d245d534830378a87259&amp;quot;}' 'http://192.168.1.42:8000/api/v2/pipeline/'&lt;br /&gt;
 {&lt;br /&gt;
    &amp;quot;create_default_locations&amp;quot;: true,&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Test pipeline&amp;quot;,&lt;br /&gt;
    &amp;quot;remote_name&amp;quot;: &amp;quot;192.168.1.42&amp;quot;,&lt;br /&gt;
    &amp;quot;resource_uri&amp;quot;: &amp;quot;/api/v2/pipeline/99354557-9e6e-4918-9fe3-a65b00ecb199/&amp;quot;,&lt;br /&gt;
    &amp;quot;uuid&amp;quot;: &amp;quot;99354557-9e6e-4918-9fe3-a65b00ecb199&amp;quot;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Get pipeline details ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/pipeline/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': None&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;: Pipeline description&lt;br /&gt;
** &amp;lt;code&amp;gt;remote_name&amp;lt;/code&amp;gt;: IP or hostname of the pipeline. For use in API calls&lt;br /&gt;
** &amp;lt;code&amp;gt;resource_uri&amp;lt;/code&amp;gt;: URI for this pipeline in the API&lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the pipeline&lt;br /&gt;
&lt;br /&gt;
== Space ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: Is there no way to create Spaces in the API?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Get all spaces ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/space/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;access_protocol&amp;lt;/code&amp;gt;: Protocol that the [[Storage Service#Space | Space]] uses. Must be searched based on the database code.&lt;br /&gt;
** &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;: Space's path&lt;br /&gt;
** &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt;: Maximum size in bytes. Can use greater than (size__gt=1024), less than (size__lt=1024), and other Django [https://docs.djangoproject.com/en/1.8/ref/models/querysets/#field-lookups field lookups].&lt;br /&gt;
** &amp;lt;code&amp;gt;used&amp;lt;/code&amp;gt;: Bytes stored in this space. Can use greater than (size__gt=1024), less than (size__lt=1024), and other Django [https://docs.djangoproject.com/en/1.8/ref/models/querysets/#field-lookups field lookups].&lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the Space&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;meta&amp;lt;/code&amp;gt;: Metadata on the response: number of hits, pagination information&lt;br /&gt;
** &amp;lt;code&amp;gt;objects&amp;lt;/code&amp;gt;: List of spaces. See [[#Get space details]] for format&lt;br /&gt;
&lt;br /&gt;
Returns information about all the spaces in the system.  Can be [http://django-tastypie.readthedocs.io/en/latest/resources.html#basic-filtering filtered] by several fields: access protocol, path, size, amount used, UUID and verified status. Disabled spaces are not returned.&lt;br /&gt;
&lt;br /&gt;
=== Get space details ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/space/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': None&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;access_protocol&amp;lt;/code&amp;gt;: Database code for the access protocol&lt;br /&gt;
** &amp;lt;code&amp;gt;last_verified&amp;lt;/code&amp;gt;: Date of last verification. This is a stub feature&lt;br /&gt;
** &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;: Space's path&lt;br /&gt;
** &amp;lt;code&amp;gt;resource_uri&amp;lt;/code&amp;gt;: URI to the resource in the API&lt;br /&gt;
** &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt;: Maximum size of the space in bytes.&lt;br /&gt;
** &amp;lt;code&amp;gt;used&amp;lt;/code&amp;gt;: Bytes stored in this space. &lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the space&lt;br /&gt;
** &amp;lt;code&amp;gt;verified&amp;lt;/code&amp;gt;: If the space is verified. This is a stub feature&lt;br /&gt;
** Other space-specific fields&lt;br /&gt;
&lt;br /&gt;
=== Browse space path ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/space/&amp;lt;UUID&amp;gt;/browse/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;: Path inside the Space to look&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;entries&amp;lt;/code&amp;gt;: List of entries at path, files or directories&lt;br /&gt;
** &amp;lt;code&amp;gt;directories&amp;lt;/code&amp;gt;: List of directories in path. Subset of `entries`.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffffcc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Version 1: Returns paths as strings&lt;br /&gt;
Version 2: Returns all paths base64 encoded&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Location ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: Is there no way to create Locations in the API?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Get all locations ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/location/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
&lt;br /&gt;
=== Get location details ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/location/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
&lt;br /&gt;
=== Move files to this location ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/location/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_location&amp;lt;/code&amp;gt;: URI of the Location the files should be moved from&lt;br /&gt;
** &amp;lt;code&amp;gt;pipeline&amp;lt;/code&amp;gt;: URI of the [[Storage Service#Pipeline | pipeline]]. Both Locations must be associated with this pipeline.&lt;br /&gt;
** &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt;: List of dicts containing &amp;lt;code&amp;gt;source&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;destination&amp;lt;/code&amp;gt;.  The source and destination are paths relative to their Location of the files to be moved.&lt;br /&gt;
&lt;br /&gt;
Intended for use with creating Transfers, SIPs, etc and other cases where files need to be moved but not tracked by the storage service.&lt;br /&gt;
&lt;br /&gt;
=== Browse location path ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/location/&amp;lt;UUID&amp;gt;/browse/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;: Path inside the Location to look&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;entries&amp;lt;/code&amp;gt;: List of entries in `path`, files or directories&lt;br /&gt;
** &amp;lt;code&amp;gt;directories&amp;lt;/code&amp;gt;: List of directories in `path`. Subset of `entries`.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffffcc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Version 1: Returns paths as strings&lt;br /&gt;
Version 2: Returns all paths base64 encoded&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== SWORD collection ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/location/&amp;lt;UUID&amp;gt;/sword/collection/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET, POST&lt;br /&gt;
&lt;br /&gt;
See [[Sword API]] for details&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Package ==&lt;br /&gt;
&lt;br /&gt;
=== Get all packages ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
&lt;br /&gt;
=== Create new package ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON. Fields for a new package:&lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the new package&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_location&amp;lt;/code&amp;gt;: URI of the Location where the package is currently&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_path&amp;lt;/code&amp;gt;: Path to the package, relative to the origin_location&lt;br /&gt;
** &amp;lt;code&amp;gt;current_location&amp;lt;/code&amp;gt;: URI of the Location where the package should be stored&lt;br /&gt;
** &amp;lt;code&amp;gt;current_path&amp;lt;/code&amp;gt;: Path where the package should be stored, relative to the current_location&lt;br /&gt;
** &amp;lt;code&amp;gt;package_type&amp;lt;/code&amp;gt;: Type of package this is. One of: &amp;lt;code&amp;gt;AIP&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;AIC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DIP&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;transfer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SIP&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;deposit&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt;: Size of the package&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_pipeline&amp;lt;/code&amp;gt;: URI of the pipeline the package is from&lt;br /&gt;
** &amp;lt;code&amp;gt;related_package_uuid&amp;lt;/code&amp;gt;: UUID of a package that is related to this one. E.g. UUID of a DIP when storing an AIP&lt;br /&gt;
&lt;br /&gt;
Creates a database entry tracking the package (AIP, transfer, etc).  If the package is an AIP, DIP or AIC and the current_location is an AIP or DIP storage location it also moves the files from the source to destination location.  If the package is a Transfer and the current_location is transfer backlog, it is also moved.&lt;br /&gt;
&lt;br /&gt;
This is handled through the modified &amp;lt;code&amp;gt;obj_create&amp;lt;/code&amp;gt; function, which calls &amp;lt;code&amp;gt;Package.store_aip&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Package.backlog_transfer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Get package details ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
&lt;br /&gt;
=== Update package contents ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': PUT&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;reingest&amp;lt;/code&amp;gt;: Flag to mark that this is reingest. Reduces chance to accidentally modify an AIP.&lt;br /&gt;
** &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt;: UUID of the existing package&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_location&amp;lt;/code&amp;gt;: URI of the Location where the package is currently&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_path&amp;lt;/code&amp;gt;: Path to the package, relative to the origin_location&lt;br /&gt;
** &amp;lt;code&amp;gt;current_location&amp;lt;/code&amp;gt;: URI of the Location where the package should be stored&lt;br /&gt;
** &amp;lt;code&amp;gt;current_path&amp;lt;/code&amp;gt;: Path where the package should be stored, relative to the current_location&lt;br /&gt;
** &amp;lt;code&amp;gt;package_type&amp;lt;/code&amp;gt;: Type of package this is. One of: &amp;lt;code&amp;gt;AIP&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;AIC&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt;: Size of the package&lt;br /&gt;
** &amp;lt;code&amp;gt;origin_pipeline&amp;lt;/code&amp;gt;: URI of the pipeline the package is from.  This must be the same pipeline reingest was started on (tracked through &amp;lt;code&amp;gt;Package.misc_attributes.reingest_pipeline&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Updates the contents of a package during reingest.  If the package is an AIP or AIC, currently stored in an AIP storage location, and the 'reingest' parameter is set, it will call &amp;lt;code&amp;gt;Package.finish_reingest&amp;lt;/code&amp;gt; and merge the new AIP with the existing one.&lt;br /&gt;
&lt;br /&gt;
This is implemented using a modified &amp;lt;code&amp;gt;obj_update&amp;lt;/code&amp;gt; which calls &amp;lt;code&amp;gt;obj_update_hook&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Update package metadata ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': PATCH&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;reingest&amp;lt;/code&amp;gt;: Pipeline UUID or None.&lt;br /&gt;
&lt;br /&gt;
Used to update metadata stored in the database for the package.  Currently, this is used to update the reingest status.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: Currently, this always sets Package.misc_attributes.reingest to None, regardless of what value was actually passed in.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is implemented using a modified &amp;lt;code&amp;gt;obj_update&amp;lt;/code&amp;gt; which calls &amp;lt;code&amp;gt;obj_update_hook&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;update_in_place&amp;lt;/code&amp;gt; also helps.&lt;br /&gt;
&lt;br /&gt;
=== Delete package request ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/delete_aip/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;event_reason&amp;lt;/code&amp;gt;: Reason for deleting the AIP&lt;br /&gt;
** &amp;lt;code&amp;gt;pipeline&amp;lt;/code&amp;gt;: URI of the pipeline the delete request is from&lt;br /&gt;
** &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt;: User ID requesting the deletion. This is the ID of the user on the pipeline, and must be an integer greater than 0.&lt;br /&gt;
** &amp;lt;code&amp;gt;user_email&amp;lt;/code&amp;gt;:  Email of the user requesting the deletion.&lt;br /&gt;
&lt;br /&gt;
=== Recover AIP request ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/recover_aip/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;event_reason&amp;lt;/code&amp;gt;: Reason for recovering the AIP&lt;br /&gt;
** &amp;lt;code&amp;gt;pipeline&amp;lt;/code&amp;gt;: URI of the pipeline the recovery request is from&lt;br /&gt;
** &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt;: User ID requesting the recovery. This is the ID of the user on the pipeline, and must be an integer greater than 0.&lt;br /&gt;
** &amp;lt;code&amp;gt;user_email&amp;lt;/code&amp;gt;:  Email of the user requesting the recovery.&lt;br /&gt;
&lt;br /&gt;
=== Download single file ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/extract_file/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET, HEAD&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;relative_path_to_file&amp;lt;/code&amp;gt;: Path to the file to download, relative to the package path.&lt;br /&gt;
* '''Response''': Stream of the requested file&lt;br /&gt;
&lt;br /&gt;
Returns a single file from the Package.  If the package is compressed, it downloads the whole AIP and extracts it.&lt;br /&gt;
&lt;br /&gt;
This responds to HEAD because AtoM uses HEAD to check for the existence of a file. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: HEAD and GET should not perform the same functions. HEAD should be updated to not return the file, and to only check for existence.  Currently, the storage service has no way to check if a file exists except by downloading and extracting this AIP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the package is in [[Storage Service#Arkivum | Arkivum]], the package may not actually be available.  This endpoint checks if the package is locally available. If it is, it is returned as normal. If not, it returns &amp;lt;code&amp;gt;202&amp;lt;/code&amp;gt; and emails the administrator about the attempted access.&lt;br /&gt;
&lt;br /&gt;
=== Download package ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/download/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/download/&amp;lt;chunk number&amp;gt;/&amp;lt;/code&amp;gt; (for [[Storage Service#LOCKSS-o-matic | LOCKSS]] harvesting)&lt;br /&gt;
* '''Verb''': GET, HEAD&lt;br /&gt;
* '''Parameters''': None&lt;br /&gt;
* '''Response''': Stream of the package&lt;br /&gt;
&lt;br /&gt;
Returns the entire package as a single file.  If the AIP is uncompressed, create one file by using `tar`.&lt;br /&gt;
&lt;br /&gt;
If the download URL has a chunk number, it will attempt to serve the LOCKSS chunk specified for that package. If the package is not in LOCKSS, it will return the the whole package.&lt;br /&gt;
&lt;br /&gt;
This responds to HEAD because AtoM uses HEAD to check for the existence of a file. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: HEAD and GET should not perform the same functions. HEAD should be updated to not return the file, and to only check for existence.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the package is in [[Storage Service#Arkivum | Arkivum]], the package may not actually be available.  This endpoint checks if the package is locally available. If it is, it is returned as normal. If not, it returns &amp;lt;code&amp;gt;202&amp;lt;/code&amp;gt; and emails the administrator about the attempted access.&lt;br /&gt;
&lt;br /&gt;
=== Get pointer file ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/pointer_file/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': None&lt;br /&gt;
* '''Response''': Stream of the pointer file.&lt;br /&gt;
&lt;br /&gt;
=== Check fixity ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/check_fixity/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Parameters''': Query string parameters&lt;br /&gt;
** &amp;lt;code&amp;gt;force_local&amp;lt;/code&amp;gt;: If true, download and run fixity on the AIP locally, instead of using the Space-provided fixity if available.&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;success&amp;lt;/code&amp;gt;: True if the verification succeeded, False if the verification failed, None if the scan could not start&lt;br /&gt;
** &amp;lt;code&amp;gt;message&amp;lt;/code&amp;gt;: Human-readable string explaining the report; it will be empty for successful scans.&lt;br /&gt;
** &amp;lt;code&amp;gt;failures&amp;lt;/code&amp;gt;: List of 0 or more errors&lt;br /&gt;
** &amp;lt;code&amp;gt;timestamp&amp;lt;/code&amp;gt;: ISO-formated string with the datetime of the last fixity check. If the check was performed by an external system, this will be provided by that system. If not provided,or on error, it will be None.&lt;br /&gt;
&lt;br /&gt;
=== AIP storage callback request ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/send_callback/post_store/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
&lt;br /&gt;
Request to call any Callbacks configured to run post-storage for this AIP.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background-color:#ffeecc;&amp;quot; cellpadding=&amp;quot;10&amp;quot;;&lt;br /&gt;
| Improvement Note: This only works on locally available AIPs (AIPs stored in Spaces that are available via a UNIX filesystem layer).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Get file information for package ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/contents/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET&lt;br /&gt;
* '''Response''': JSON&lt;br /&gt;
** &amp;lt;code&amp;gt;success&amp;lt;/code&amp;gt;: True&lt;br /&gt;
** &amp;lt;code&amp;gt;package&amp;lt;/code&amp;gt;: UUID of the package&lt;br /&gt;
** &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt;: List of dictionaries with file information. Each dictionary has:&lt;br /&gt;
*** &amp;lt;code&amp;gt;source_id&amp;lt;/code&amp;gt;: UUID of the file to index&lt;br /&gt;
*** &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;: Relative path of the file inside the package&lt;br /&gt;
*** &amp;lt;code&amp;gt;source_package&amp;lt;/code&amp;gt;: UUID of the SIP this file is from&lt;br /&gt;
*** &amp;lt;code&amp;gt;checksum&amp;lt;/code&amp;gt;: Checksum of the file, or an empty string&lt;br /&gt;
*** &amp;lt;code&amp;gt;accessionid&amp;lt;/code&amp;gt;: Accession number, or an empty string&lt;br /&gt;
*** &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt;: UUID of the Archivematica dashboard this is from&lt;br /&gt;
&lt;br /&gt;
Returns metadata about every file within the package.&lt;br /&gt;
&lt;br /&gt;
=== Update file information for package ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/contents/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': PUT&lt;br /&gt;
* '''Parameters''': JSON list of dictionaries with information on the files to be added. Each dict must have the following attributes:&lt;br /&gt;
** &amp;lt;code&amp;gt;relative_path&amp;lt;/code&amp;gt;: Relative path of the file inside the package&lt;br /&gt;
** &amp;lt;code&amp;gt;fileuuid&amp;lt;/code&amp;gt;: UUID of the file to index&lt;br /&gt;
** &amp;lt;code&amp;gt;accessionid&amp;lt;/code&amp;gt;: Accession number, or an empty string&lt;br /&gt;
** &amp;lt;code&amp;gt;sipuuid&amp;lt;/code&amp;gt;: UUID of the SIP this file is from&lt;br /&gt;
** &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt;: UUID of the Archivematica dashboard this is from&lt;br /&gt;
&lt;br /&gt;
Adds a set of files to a package.&lt;br /&gt;
&lt;br /&gt;
=== Delete file information for package ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/contents/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': DELETE&lt;br /&gt;
&lt;br /&gt;
Removes all file records associated with this package.&lt;br /&gt;
&lt;br /&gt;
=== Query file information on packages ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/metadata/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': GET, POST&lt;br /&gt;
* '''Parameters''': Query string parameters.  Must have at least one, but not all are required&lt;br /&gt;
** &amp;lt;code&amp;gt;relative_path&amp;lt;/code&amp;gt;: Relative path of the file inside the package&lt;br /&gt;
** &amp;lt;code&amp;gt;fileuuid&amp;lt;/code&amp;gt;: UUID of the file&lt;br /&gt;
** &amp;lt;code&amp;gt;accessionid&amp;lt;/code&amp;gt;: Accession number&lt;br /&gt;
** &amp;lt;code&amp;gt;sipuuid&amp;lt;/code&amp;gt;: UUID of the SIP this file is from&lt;br /&gt;
* '''Response''': JSON. List of dicts with file information about the files that match the query.&lt;br /&gt;
** &amp;lt;code&amp;gt;accessionid&amp;lt;/code&amp;gt;: Accession number, or an empty string&lt;br /&gt;
** &amp;lt;code&amp;gt;file_extension&amp;lt;/code&amp;gt;: File extension&lt;br /&gt;
** &amp;lt;code&amp;gt;filename&amp;lt;/code&amp;gt;: Name of the file, sans path.&lt;br /&gt;
** &amp;lt;code&amp;gt;relative_path&amp;lt;/code&amp;gt;: Relative path of the file inside the package&lt;br /&gt;
** &amp;lt;code&amp;gt;fileuuid&amp;lt;/code&amp;gt;: UUID of the file to index&lt;br /&gt;
** &amp;lt;code&amp;gt;sipuuid&amp;lt;/code&amp;gt;: UUID of the SIP this file is from&lt;br /&gt;
** &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt;: UUID of the Archivematica dashboard this is from&lt;br /&gt;
&lt;br /&gt;
=== Reingest AIP ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/reingest/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Verb''': POST&lt;br /&gt;
* '''Parameters''': JSON body&lt;br /&gt;
** &amp;lt;code&amp;gt;pipeline&amp;lt;/code&amp;gt;: UUID of the pipeline to reingest on&lt;br /&gt;
** &amp;lt;code&amp;gt;reingest_type&amp;lt;/code&amp;gt;: Type of reingest to start. One of &amp;lt;code&amp;gt;METADATA_ONLY&amp;lt;/code&amp;gt; (metadata-only reingest), &amp;lt;code&amp;gt;OBJECTS&amp;lt;/code&amp;gt; (partial reingest), &amp;lt;code&amp;gt;FULL&amp;lt;/code&amp;gt; (full reingest)&lt;br /&gt;
** &amp;lt;code&amp;gt;processing_config&amp;lt;/code&amp;gt;: Optional. Name of the processing configuration to use on full reingest&lt;br /&gt;
&lt;br /&gt;
=== SWORD endpoints ===&lt;br /&gt;
&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/sword/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/sword/media/&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''URL''': &amp;lt;code&amp;gt;/api/v2/file/&amp;lt;UUID&amp;gt;/sword/state/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See [[Sword API]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Development documentation]]&lt;/div&gt;</summary>
		<author><name>Jraddaoui</name></author>
	</entry>
</feed>