# HG changeset patch # User dwinter # Date 1361888527 -3600 # Node ID 834706423ac1042d43b2081e1dfa014a5fdb1897 initial diff -r 000000000000 -r 834706423ac1 .project --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.project Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,17 @@ + + + zopeSolr + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff -r 000000000000 -r 834706423ac1 .pydevproject --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.pydevproject Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,8 @@ + + + +/zopeSolr + +python 2.7 +pythonZope13 + diff -r 000000000000 -r 834706423ac1 .settings/org.eclipse.core.resources.prefs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.core.resources.prefs Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 +encoding/zopeSolr.py=utf-8 diff -r 000000000000 -r 834706423ac1 __init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/__init__.py Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,15 @@ +import zopeSolr + +def initialize(context): + """initialize OSAS""" + + + context.registerClass( + zopeSolr.ZopeSolr, + constructors = ( + zopeSolr.manage_addZopeSolrForm, + zopeSolr.manage_addZopeSolr + ) + ) + + diff -r 000000000000 -r 834706423ac1 test/test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test.py Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,30 @@ +''' +Created on 11.02.2013 + +@author: dwinter +''' + +import sunburnt +import httplib + +class TestSolr: + + solr = None + def __init__(self): + self.solr=sunburnt.SolrInterface(url="http://127.0.0.1:8983/solr/mpiwgweb") + +if __name__ == '__main__': + + sl = TestSolr() + + #params={facet=true&facet.field=description&facet.field=main_content&start=0&q=main_content:knowledge+AND+renn+AND+description:*+AND+main_content:*&rows=10} hits=113 status=0 QTime=66 + + x= {"qt":"tvrh","tv":"on","fl":"main_content","tv.tf":True} + + fq = sl.solr.query(urlNorm="http://127.0.0.1:18080/www_neu/en/news/features/feature25",**x).facet_by("main_content") + x = fq.execute() + + #http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=xml&tv=on&qt=tvrh&fl=main_content&tv.tf=true + #http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=json&tv=on&qt=tvrh&fl=main_content&tv.tf=true + print x.facet_counts.facet_fields['main_content'] + \ No newline at end of file diff -r 000000000000 -r 834706423ac1 test/test2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test2.py Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,23 @@ +import httplib2 +import xml.etree.ElementTree as ET + +h = httplib2.Http() +resp, content = h.request("http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=xml&tv=on&qt=tvrh&fl=main_content&tv.tf=true") + +root = ET.fromstring(content) +uri = "http://127.0.0.1:18080/www_neu/de/aktuelles/features/feature28" +fieldname="main_content" +tvs = root.findall(".//lst[@name='termVectors']/lst[@name='%s']/lst[@name='%s']/lst"%(uri,fieldname)) +print tvs +for tv in tvs: + word = tv.attrib['name'] + for f in tv.findall("./int[@name='tf']"): + fre = f.text + + print word,fre + + + + + +#urlNormhttp://127.0.0.1:18080/www_neu/de/aktuelles/features/feature2812 \ No newline at end of file diff -r 000000000000 -r 834706423ac1 zopeSolr.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zopeSolr.py Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,436 @@ +# -*- coding: utf-8 -*- + +#Verbindet Zope mit solr. Vorraussetzung ist das Paket sunburnt, @see http://opensource.timetric.com/sunburnt/ + + +from OFS.SimpleItem import SimpleItem +from Products.PageTemplates.PageTemplateFile import PageTemplateFile +import pysolr +import os.path +import sunburnt +from Globals import package_home +import httplib2 +import urllib +import re +import xml.etree.ElementTree as ET +import json + +#Worte die nicht in der Termliste angezeigt werden sollen #TODO: make this configurable + +STOPLIST={'main_content':['forward','drucken','history','science','part','publications','projects', + 'project','new','geschichte','institute','related','boltzmannstraße','14195'], + + 'title':['bd','10','11','12','18','19','20','abt','ad','di','history','geschichte','science'] + } + + + +def zptFile(self, path, orphaned=False): + """returns a page template file from the product""" + if orphaned: + # unusual case + pt=PageTemplateFile(os.path.join(package_home(globals()), path)) + else: + + pt=PageTemplateFile(os.path.join(package_home(globals()), path)).__of__(self) + return pt + + + +class ZopeSolr(SimpleItem): + + + meta_type="ZopeSolr" + + manage_options= ({'label':'Main Config','action': 'changeMain'},) + SimpleItem.manage_options + + def __init__(self,id,title,solrURL): + self.id=id + self.title=title + self.solrURL=solrURL #URL einer solr instance bzw. einer collection, falls nicht die default collection benutzt werden soll + + + #Verbinde mit der solt Instance + + def connect(self): + self._v_solr=sunburnt.SolrInterface(url=self.solrURL) + + #erzeuge einen Link, insbesonder für faceted suche + #@param search: ist suchparameter, wird einfach als search=%s weitergereicht + #@param facetSearch: bekommt einen hash (feldname, liste der suchworte) + + def generateLink(self,search,facetSearch={},ranges={}): + ret="?search=%s"%search + + for facet in facetSearch.keys(): + + searchTerms = facetSearch[facet] + + if isinstance(searchTerms, basestring): + searchTerms=[searchTerms] + + for searchTerm in searchTerms: + try: + ret+="&%s_fc=%s"%(facet,searchTerm.encode('utf-8')) + except: + ret+="&%s_fc=%s"%(facet,searchTerm) + + for key,values in ranges.items(): + + if isinstance(values, basestring): + values=[values] + + for value in values: + ret+="&%s_rg=%s"%(key,value) + + + return ret + + #hilfsmethode erzeuget immer eine liste von einträgen + def getList(self,param): + if isinstance(param, basestring): + param=[param] + + return param + + #erzeugt analog zu den Ranges in Velocity für ein numerisches Feld die Suche in ranges + #@param field:ist der name des Feldes in dem in Ranges gesucht werden soll + #@param begin anfang der ranges + #@param end ende der Raanges + #@param increment größe eines ranges + # für alle andere paramter @see prepareSearch + #@return gibt ein hash: mit range -> anzahl der Treffer in dem Ranage. e.g 1921-1950 -> 21 + def ranges(self,field,begin,end,increment,solrQuery="",facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True): + + q=self.prepareSearch(solrQuery,facetFields,start,rows,facetSearch,sortFacets) + + res={} + if not getattr(self,'_v_solr_',None): + self.connect() + + + + for x in range(begin,end,increment): + query={} + #query["%s__gt"%field]=x + #TODO __gt scheint nicht zu funktionieren wird zu gte (???) + query["%s__gte"%field]=int(x)+1 + + query["%s__lte"%field]=x+increment + + result = q.query(**query).execute() + + res["%s-%s"%(x,x+increment)]=result.result.numFound + + return res + + + #prepareSearch erzeugt die Suchabfrage + + #solrQuery sucht im in schema.xml bzw. solrconfig.xml festgelegt generischen Feld, hierbei werden mit blanks getrennte eintrage in "AND" zerlegt. + #TODO: erlaube auch suche nach phrasen mit "" + #facetFields:Liste der Felder, nach denen facitiert werden sollen, wirdn in facet_by in solrburn uebersetzt. + #start: Erste Eintrag für paginierung, anzahl der Treffer + #rows: anzahl der Treffer + #facetSearch: Hash mit Feldnamen: suchwort oder Feldname: liste von Suchworten, hast wird direkt an query von solrburnt weitergegeben, + #sortfacets: if true, dann werden die Ergebnisse der facetierten suche alphabetisch sortiert, ACHTUNG: das ist nicht gleich der Funktion in solr die Liste + #direkt sortiert zurückzubekommen, hier werden die haufigsten Werte genommen (einstellt in solrconfig.xml) und dann nur diese sortiert! + #ausserdem werden beim sortieren, die stopworte gefiltert! + # + #neben den direkten parameter koennen auch parameter fuer die facetierte Suche über FORM im REQUEST übergeben werden, diese Felder müssen dann + #mit "_fc" enden. + # gibt als ergebnis den folgenden Hash, so wie in http://opensource.timetric.com/sunburnt/queryingsolr.html#executing-queries-and-interpreting-the-response, + # http://opensource.timetric.com/sunburnt/queryingsolr.html#highlighting + # und http://opensource.timetric.com/sunburnt/queryingsolr.html#faceting + #dokumentiert. Highlighting selbst wird in sorlconfig.xml konfiguriert. + #return ein Queryobjet, zur eigentlichen Suche muss darauf noch execute ausgeführt werden. + + def prepareSearch(self,solrQuery,facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True): + "search solr" + + + ranges={} + ## deal with a form + if self.REQUEST: + constr = self.REQUEST.form + print constr + for field in constr.keys(): + + #facetes + if field.endswith("_fc"): + if facetSearch is None: + facetSearch={} + + vals = constr[field] + if not isinstance(vals,basestring): + + vals=[x.decode('utf-8') for x in vals] + else: + vals=vals.decode('utf-8') + facetSearch[field.replace("_fc",'')]=vals + #ranges form a-b + + + if field.endswith("_rg"): + + + splitted = "_".split(field) + if len(splitted)==2: + #ranges[field.replace("_rg","__gt")]=splitted[0] + #TODO __gt scheint nicht zu funktionieren wird zu gte (???) + ranges[field.replace("_rg","__gte")]=int(splitted[0])+1 + ranges[field.replace("_rg","__lte")]=splitted[1] + + + + #teste verbindung zu solr + if not getattr(self,'_v_solr_',None): + self.connect() + + + + solrQuery = solrQuery.decode('utf-8') + + #teile die suche nach " " daraus wird dann eine AND suche + #TODO: sollte flexibler sein. insbesondere phrasen + splitted= solrQuery.split(" ") + + res = self._v_solr + + + + + res=res.query(splitted) + if len(ranges.keys())>0: + res=res.query(ranges) + + + #ubergebe alle weiteren feld an die suche. + if facetSearch: + for key,vals in facetSearch.items(): + + if key.endswith("_rg"): + + if not isinstance(vals,basestring): + + vals=[x.decode('utf-8') for x in vals] + else: + vals=[vals.decode('utf-8')] + + for val in vals: + + splitted = val.split("-") + print splitted + if len(splitted)==2: + #TODO __gt scheint nicht zu funktionieren wird zu gte (???) + facetSearch[key.replace("_rg","__gte")]=int(splitted[0])+1 + facetSearch[key.replace("_rg","__lte")]=splitted[1] + print "I AM HERE 3" + print facetSearch + del facetSearch[key] # loesche das urspuerngliche feld + + else: + val = [x for x in vals if x!="*"] #siehe oben + facetSearch[key]=val + + + res=res.query(**facetSearch) + + + #wenn facetField existieren dann rufe facetierung auf + if len(facetFields)>0: + #for facet in facetFields: + + # res = res.facet_by(facet) + res = res.facet_by(facetFields) + + #res=res.paginate(start=start, rows=rows).highlight("main_content") + + res=res.paginate(start=start, rows=rows) + + + + res=res.paginate(start=start, rows=rows).highlight() + + return res + + + #für die parameter @see prepareSearch + #erzeugt eine Suchabfrage und führt diese aus. + #return {"result":response.result, "hl":response.highlighting,"facetFields":facetedFields} + + def search(self,solrQuery,facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True): + + res=self.prepareSearch(solrQuery,facetFields,start,rows,facetSearch,sortFacets) + response= res.execute() + + #speichere faceted fields + if len(facetFields)>0: + facetedFields=response.facet_counts.facet_fields + + if sortFacets: + facetedFields=self.sortFacetedFields(facetedFields) + + else: + facetedFields={} + + + + + return {"result":response.result, "hl":response.highlighting,"facetFields":facetedFields} + + + + #hilfsmethode zum sortieren über der ranges + def sortRanges(self,ranges): + x=list(ranges) + x.sort() + return x + + #sortiert die Werte der FacetedFields + #(facetedFields ist ein hast mit feldname -> liste der (wert für das feld, anzahl der treffer für den wert) + #ausserdem werden die werte gemäß des angegebenen Filter gefiltert. + + def sortFacetedFields(self, facetedFields,filter=STOPLIST): + ret={} + + def cmpTuple(x,y): + return cmp(x[0],y[0]) + + + for key in facetedFields.keys(): + ls = facetedFields[key] + ls.sort(cmpTuple) + ret[key]=ls + + if filter.get(key,None): + ls2=[] + for x in ls: + if x[0].encode('utf-8') not in filter[key]: + ls2.append(x) + ret[key]=ls2 + + + ret[key]=[x for x in ret[key] if x[1]!=0] + return ret + + def changeMain(self,solrURL=None,title=None,REQUEST=None,RESPONSE=None): + """change main settings""" + if solrURL: + self.solrURL=solrURL + self.title=title + self._v_solr=sunburnt.SolrInterface(url=solrURL) + + if RESPONSE is not None: + RESPONSE.redirect('manage_main') + + + else: + pt=zptFile(self, 'zpt/ChangeZopeSolr.zpt') + return pt() + + #sucht die je nach einsteillung in solrconfig.xml Werte mit den häufigsten Treffern oder alphabetisch sortiert zu einem + #bestimmten eintrag aus solr + #gedacht ist die methoden für die Anwendung nach dem Harvesten einer Website, es wird daher davon ausgegangen, dass sie hinter der + #idfield eine url steht. Wir es z.b. bei Nutch passiert. index.htm/index_html als Teil der url wird dabei unterdrückt (analog zu den harbest einstellunge für + # nutch für zope webseiten. + #@param @idfield is hierbei der Name des Feldes, das in solrschema als id defniert wurde + #field der Feldname von dem die Treffer gesurcht wernde sollen + #url die url des textes + + def getTermsAsJSON(self,idfield,field,url): + """getTerms""" + + ret=[] + h = httplib2.Http() + url = url.replace("/index.html","").replace("/index_html","") + if url[-1]=="/": + url=url[0:-1] + + urlq=url.replace(":","\:") + urlq=urlq.replace("/","\/") + + q ="http://localhost:8983/solr/mpiwgweb/select?q=%s:%s&wt=xml&tv=on&qt=tvrh&fl=%s&tv.tf=true"%(idfield,urlq,field) + + resp, content = h.request(q) + + root = ET.fromstring(content) + #uri = "http://127.0.0.1:18080/www_neu/de/aktuelles/features/feature28" + + xpstr = ".//lst[@name='termVectors']/lst[@name='%s']/lst[@name='%s']/lst"%(url,field) + + + tvs = root.findall(xpstr) + + for tv in tvs: + wd = tv.attrib['name'] + for f in tv.findall("./int[@name='tf']"): + fre = f.text + + + if int(fre)>2: + + ret.append('{"text":"%s","size":%s}'%(wd,fre)) + + retStr="["+",".join(ret)+"]" + + return retStr + + #tauscht im request die in neewparams angegeben parameter aus. + def replaceParam(self, newparams): + x = self.REQUEST.form.copy() + + for key,value in newparams.items(): + x[key]=value + + + retls=[] + for k,v in x.items(): + if not isinstance(v,basestring): + for y in v: + retls.append((k,y)) + else: + retls.append((k,v)) + + return "?"+"&".join(["%s=%s"%(k,urllib.quote_plus(v,'/')) for (k, v) in retls]) + + + + + #ruft @set ranges aus, gibt das ergebnis als json zurück + def getRangesAsJSON(self,field,begin,end,increment): + """ getRangesAsJSON""" + res = self.ranges(field, int(begin), int(end), int(increment)) + return json.dumps(res) + + + #return only the values of resultList whicht start with startLetter or if starLetterNonAscii all values which + #start with an non ascii character + def filter (self,resultList,startLetter=None,startLetterNonAscii=0): + if startLetter: + + matchStr = "[\[\]'\"]*"+startLetter + ls = [x for x in resultList if re.match(matchStr,x[0])] + + if startLetterNonAscii ==1: + + ls = [x for x in resultList if not re.match("[\[\]'\"a-zA-Z].*",x[0])] + return ls + +def manage_addZopeSolrForm(self): + """Form for external Links""" + pt=zptFile(self, 'zpt/AddZopeSolr.zpt') + return pt() + + +def manage_addZopeSolr(self,id,title,solrURL,RESPONSE=None): + """Add an external Link""" + + newObj=ZopeSolr(id,title,solrURL) + + self._setObject(id,newObj) + + + if RESPONSE is not None: + RESPONSE.redirect('manage_main') + \ No newline at end of file diff -r 000000000000 -r 834706423ac1 zpt/AddZopeSolr.zpt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zpt/AddZopeSolr.zpt Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,14 @@ +

Header

+

Add a new Ressource

+
+ + + + + + +
ID:
Title:
url:
+

+
+

Footer

+ \ No newline at end of file diff -r 000000000000 -r 834706423ac1 zpt/ChangeZopeSolr.zpt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zpt/ChangeZopeSolr.zpt Tue Feb 26 15:22:07 2013 +0100 @@ -0,0 +1,14 @@ +

Header

+

Add a new Ressource

+
+ + + + + +
Title:
url:
+

+
+ +

Footer

+ \ No newline at end of file