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

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

Extend eXistInterface search method to allow more granulated searches of
atom data - for use in the atom editor pages. Add new method to dynamically
generate the required search xquery. NB, this is done to avoid using
full search query when not needed since this adds significantly to the
search time. Add new search tests.

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        r=self.executeChunkedQuery(xquery,start,howmany)
114        x=xmlHandler2.xmlHandler(str(r),string=1)
115        h=x.tree.get('hits')
116        self.results=[]
117        self.serverSessionID=''
118        if h is None:
119            self.hits=0
120            self.start=0
121            self.howmany=0
122            self.error=['No results for [%s]'%term,]
123        else:
124            self.hits=int(h)
125            self.error=None
126            self.start=int(x.tree.get('start'))
127            self.howmany=int(x.tree.get('count'))
128            slist=x.tree.findall('summary')
129            for s in slist:
130                t=edict(id=s.find('id').text,name=s.find('name').text,\
131                        type=s.find('type').text)
132                # add extra stuff for atom search (NB, this could be added to all
133                # but am unsure of implications elsewhere atm
134                if target == self.ATOM_TARGET:
135                    for key in ['providerID', 'created', 'href', 'subtype']:
136                        t[key] = s.find(key).text
137
138                        # adjust href to point to atom editor instead of atom view
139                        if key == 'href':
140                            t[key] = t[key].replace('view', 'editAtom')
141                   
142                self.results.append(t)
143        return self.results
144   
145
146    def __createAtomSearch(self, providerID, atomTypeID, term):
147        '''
148        Create a valid xquery search for searching atoms
149        '''
150        logging.debug("Creating xquery for searching atoms")
151        logging.debug("providerID: '%s', atomTypeID: '%s', term: '%s'" 
152                      %(providerID, atomTypeID, term))
153        xquery = "declare namespace moles='http://ndg.nerc.ac.uk/schema/moles2alpha';\n" + \
154            "declare namespace atom='http://www.w3.org/2005/Atom';\n" + \
155            "for $DE in collection('/db/atoms')//root()["
156
157        # only add the required search clauses - NB, these increase the search time
158        # significantly, so avoid unless really required
159        whereClause = []
160        if term:
161            whereClause.append(". &= '" + term + "'")
162       
163        if providerID and providerID != self.DEFAULT_ALL_VAL:
164            whereClause.append("atom:entry/moles:entity/moles:molesISO/moles:providerID = '" + providerID + "'")
165       
166        if atomTypeID and atomTypeID != self.DEFAULT_ALL_VAL:
167            whereClause.append("atom:entry/atom:category/@label = '" + atomTypeID + "'")
168           
169        if not whereClause:
170            whereClause.append(".")
171       
172        for i, clause in enumerate(whereClause):
173            if i > 0:
174                xquery += " and "
175            xquery += clause
176           
177        xquery += "] return <summary> \
178<id>{$DE/atom:entry/atom:id/text()}</id> \
179<name>{$DE/atom:entry/atom:title/text()}</name> \
180<type>{string($DE/atom:entry/atom:category[@term='ATOM_TYPE']/@label)}</type> \
181<subtype>{string($DE/atom:entry/atom:category[@term='ATOM_SUBTYPE']/@label)}</subtype> \
182<href>{string($DE/atom:entry/atom:link[@rel='self']/@href)}</href> \
183<providerID>{$DE/atom:entry/moles:entity/moles:molesISO/moles:providerID/text()}</providerID> \
184<created>{$DE/atom:entry/moles:entity/moles:molesISO/moles:created/text()}</created> \
185</summary>"
186
187        logging.debug("Created xquery: '%s'" %xquery)
188        return xquery
Note: See TracBrowser for help on using the repository browser.