source: exist/trunk/python/ndgUtils/eXistInterface.py @ 4427

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/exist/trunk/python/ndgUtils/eXistInterface.py@4427
Revision 4427, 7.8 KB checked in by cbyrom, 11 years ago (diff)

Upgrade the various 'list' xqueries - to nest all results in a single
root element - so that only one document need be retrieved to get all
results + adjust ndgDirectory to cope with processing the new results +
fix the various namespaces mentioned in the codebase to map to the
current atom/moles ones.

Line 
1from eXistConnector import *
2from ndgXqueries import ndgXqueries
3import xmlHandler2, logging
4
5class ndg_eXist(eXistConnector):
6    ''' Adds ndg methods to a "standard" exist Connector '''
7   
8    ATOM_TARGET = 'Atom'
9    DEFAULT_ALL_VAL = 'All' # value used when specifying '*' in searches
10
11    def __init__(self,db='glue.badc.rl.ac.uk',passwordFile='passwords.txt'):
12        logging.debug("Initialising connection to eXist DB ('%s')" %db)
13        try:
14            f=file(passwordFile,'r')
15        except IOError,e:
16            raise IOError('%s [looking for %s in %s]'%(e,passwordFile,os.getcwd()))
17           
18        pw={}
19        for line in f.readlines():
20            host,userid,password=line.strip().split(' ')
21            pw[host]=(userid,password)
22        f.close()
23        if db not in pw:
24            raise ValueError('Unable to find eXist password for repository [%s]'%db)
25        eXistConstants = InstanceObject(host=db,
26                                userid=pw[db][0],
27                                password=pw[db][1],
28                                base_path="/exist/servlet",
29                                xmlrpc_base_path="/exist/xmlrpc",
30                                port=8080)
31       
32        eXistConnector.__init__(self,eXistConstants)
33        self.ids={}
34        logging.debug("Connection initialised")
35       
36       
37    def __buildquery(self,query,target=None):
38        '''Create an NDG full text query '''
39        if target is not None:
40            s='''let $hits := collection('/db/%s')//root()[. &= '%s'] ;
41                 for $i in $hits
42                    return <document>{document-uri($i)}</document>'''%(target,query)
43        else: s="/*[. &='%s']"%query
44
45        return s#xmlrpclib.Binary(s)
46
47    def __buildParamSearch(self,param,value,target):
48        s="for $x in document()//%s where $x[.%s &= '%s'] return $x"%(target,param,value)
49        return s
50
51    def full_text(self,query,target=None):
52        ''' Carry out a full text search within the "target" collection '''
53        id,summary=self.executeQuery(self.__buildquery(query,target))
54        self.ids[id]=0
55        return id,summary
56
57    def retrieveNext(self,id,pos=None):
58        ''' Takes a sessionID from an existing query and gets the next document '''
59        if pos is not None: self.ids[id]=pos
60        try:
61            r=self.retrieve(id, self.ids[id])
62            self.ids[id]+=1
63            return r
64        except xmlrpclib.Fault:
65            return None
66        except KeyError:
67            return None
68   
69    def sessionRelease(self,id):
70        ''' Releases a session and removes the position counter '''
71        try:
72            self.release(id)
73            del self.ids[id]
74            return 1
75        except:
76            return 0
77   
78    def chunkedFullText(self,query,start=1,number=10,target='DIF'):
79        ''' Execute a chunked full text query and return the result
80        set '''
81        return self.executeChunkedQuery(self.__buildquery(query,target),start,number,params={})
82   
83   
84    def getDIF(self,entryID):
85        ''' Get a specific DIF document from a repository by using the entryID '''
86        xq='''for $DE in collection()/DIF[Entry_ID='%s'] return $DE'''%entryID
87        xquery='''for $DE in collection('/db/testdif1')/DIF[Entry_ID='%s'] return $DE'''%entryID
88        id,summary=self.executeQuery(xq)#xquery)
89        if summary['hits']==1:
90            r=self.retrieve(id,0,{})
91            self.sessionRelease(id)
92        else:
93            r=summary['hits']
94        return r
95       
96    def search(self,term,start=1,howmany=20, \
97               target=None,scope=None,bbox=None, \
98               dateRange=None,geoSearchType=None, \
99               providerID=None, atomTypeID=None):
100        ''' Provides a search interface that mimics the WSDL search interface, except that
101        the target used is the exist collection name, and scope, bbox and dateRange are ignored,
102        and a python summary object is returned '''
103        #select the right query according to the docType
104       
105        if target == self.ATOM_TARGET:
106            xquery = self.__createAtomSearch(providerID, atomTypeID, term)
107        else:
108            xqName={'ndg_B_metadata':'molesSummary',\
109                'NumSim':'numsimSummary'}[target]
110            xquery=ndgXqueries()[xqName]
111            xquery=xquery.replace('SEARCHSTRING',term)
112       
113        logging.info("Executing xquery search")
114        r=self.executeChunkedQuery(str(xquery),start,howmany)
115        logging.info("Search complete - processing results")
116        x=xmlHandler2.xmlHandler(str(r),string=1)
117        h=x.tree.get('hits')
118        self.results=[]
119        self.serverSessionID=''
120        if h is None:
121            self.hits=0
122            self.start=0
123            self.howmany=0
124            self.error=['No results for [%s]'%term,]
125        else:
126            self.hits=int(h)
127            self.error=None
128            self.start=int(x.tree.get('start'))
129            self.howmany=int(x.tree.get('count'))
130            slist=x.tree.findall('summary')
131            for s in slist:
132                t=edict(id=s.find('id').text,name=s.find('name').text,\
133                        type=s.find('type').text)
134                # add extra stuff for atom search (NB, this could be added to all
135                # but am unsure of implications elsewhere atm
136                if target == self.ATOM_TARGET:
137                    for key in ['providerID', 'created', 'href', 'subtype']:
138                        t[key] = s.find(key).text
139
140                        # adjust href to point to atom editor instead of atom view
141                        if key == 'href' and t[key]:
142                            t[key] = t[key].replace('view', 'editAtom')
143                           
144                        if key == 'subtype':
145                            t[key] = t[key]
146                   
147                self.results.append(t)
148        return self.results
149   
150
151    def __createAtomSearch(self, providerID, atomTypeID, term):
152        '''
153        Create a valid xquery search for searching atoms
154        '''
155        logging.debug("Creating xquery for searching atoms")
156        logging.debug("providerID: '%s', atomTypeID: '%s', term: '%s'" 
157                      %(providerID, atomTypeID, term))
158        xquery = "declare namespace moles='http://ndg.nerc.ac.uk/schema/moles2beta';\n" + \
159            "declare namespace atom='http://www.w3.org/2005/Atom';\n" + \
160            "for $DE in collection('/db/atoms')//root()["
161
162        # only add the required search clauses - NB, these increase the search time
163        # significantly, so avoid unless really required
164        whereClause = []
165        if term:
166            whereClause.append(". &= '" + term + "'")
167       
168        if providerID and providerID != self.DEFAULT_ALL_VAL:
169            whereClause.append("atom:entry/moles:entity/moles:molesISO/moles:providerID = '" + providerID + "'")
170       
171        if atomTypeID and atomTypeID != self.DEFAULT_ALL_VAL:
172            whereClause.append("atom:entry/atom:category/@label = '" + atomTypeID + "'")
173           
174        if not whereClause:
175            whereClause.append(".")
176       
177        for i, clause in enumerate(whereClause):
178            if i > 0:
179                xquery += " and "
180            xquery += clause
181           
182        xquery += "] return <summary> \
183<id>{$DE/atom:entry/atom:id/text()}</id> \
184<name>{$DE/atom:entry/atom:title/text()}</name> \
185<type>{string($DE/atom:entry/atom:category[@term='ATOM_TYPE']/@scheme)}</type> \
186<subtype>{string($DE/atom:entry/atom:category[@term='ATOM_SUBTYPE']/@scheme)}</subtype> \
187<href>{string($DE/atom:entry/atom:link[@rel='self']/@href)}</href> \
188<providerID>{$DE/atom:entry/moles:entity/moles:molesISO/moles:providerID/text()}</providerID> \
189<created>{$DE/atom:entry/moles:entity/moles:molesISO/moles:created/text()}</created> \
190</summary>"
191
192        logging.debug("Created xquery: '%s'" %xquery)
193        return xquery
Note: See TracBrowser for help on using the repository browser.