home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / rdflib / TextIndex.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  12.9 KB  |  355 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import logging
  5. _logger = logging.getLogger(__name__)
  6. import re
  7.  
  8. def get_stopdict():
  9.     '''Return a dictionary of stopwords.'''
  10.     return _dict
  11.  
  12. _words = [
  13.     'a',
  14.     'and',
  15.     'are',
  16.     'as',
  17.     'at',
  18.     'be',
  19.     'but',
  20.     'by',
  21.     'for',
  22.     'if',
  23.     'in',
  24.     'into',
  25.     'is',
  26.     'it',
  27.     'no',
  28.     'not',
  29.     'of',
  30.     'on',
  31.     'or',
  32.     'such',
  33.     'that',
  34.     'the',
  35.     'their',
  36.     'then',
  37.     'there',
  38.     'these',
  39.     'they',
  40.     'this',
  41.     'to',
  42.     'was',
  43.     'will',
  44.     'with']
  45. _dict = { }
  46. for w in _words:
  47.     _dict[w] = None
  48.  
  49. word_pattern = re.compile('(?u)\\w+')
  50. has_stop = get_stopdict().has_key
  51.  
  52. def splitter(s):
  53.     return word_pattern.findall(s)
  54.  
  55.  
  56. def stopper(s):
  57.     return _[1]
  58.  
  59.  
  60. try:
  61.     from hashlib import md5
  62. except ImportError:
  63.     from md5 import md5
  64.  
  65. from rdflib.store.IOMemory import IOMemory
  66. from rdflib import URIRef, Literal, RDF, BNode
  67. from rdflib.Namespace import NamespaceDict as Namespace
  68. from rdflib.Graph import ConjunctiveGraph
  69. from rdflib.store import TripleAddedEvent, TripleRemovedEvent
  70.  
  71. class TextIndex(ConjunctiveGraph):
  72.     """
  73.     An rdflib graph event handler than indexes text literals that are
  74.     added to a another graph.
  75.  
  76.     This class lets you 'search' the text literals in an RDF graph.
  77.     Typically in RDF to search for a substring in an RDF graph you
  78.     would have to 'brute force' search every literal string looking
  79.     for your substring.
  80.  
  81.     Instead, this index stores the words in literals into another
  82.     graph whose structure makes searching for terms much less
  83.     expensive.  It does this by chopping up the literals into words,
  84.     removing very common words (currently only in English) and then
  85.     adding each of those words into an RDF graph that describes the
  86.     statements in the original graph that the word came from.
  87.  
  88.     First, let's create a graph that will transmit events and a text
  89.     index that will receive those events, and then subscribe the text
  90.     index to the event graph:
  91.  
  92.       >>> e = ConjunctiveGraph()
  93.       >>> t = TextIndex()
  94.       >>> t.subscribe_to(e)
  95.  
  96.     When triples are added to the event graph (e) events will be fired
  97.     that trigger event handlers in subscribers.  In this case our only
  98.     subscriber is a text index and its action is to index triples that
  99.     contain literal RDF objects.  Here are 3 such triples:
  100.  
  101.       >>> e.add((URIRef('a'), URIRef('title'), Literal('one two three')))
  102.       >>> e.add((URIRef('b'), URIRef('title'), Literal('two three four')))
  103.       >>> e.add((URIRef('c'), URIRef('title'), Literal('three four five')))
  104.  
  105.     Of the three literal objects that were added, they all contain
  106.     five unique terms.  These terms can be queried directly from the
  107.     text index:
  108.     
  109.       >>> t.term_strings() ==  set(['four', 'five', 'three', 'two', 'one'])
  110.       True
  111.  
  112.     Now we can search for statement that contain certain terms.  Let's
  113.     search for 'one' which occurs in only one of the literals
  114.     provided, 'a'.  This can be queried for:
  115.  
  116.       >>> t.search('one')
  117.       set([(rdflib.URIRef('a'), rdflib.URIRef('title'), None)])
  118.  
  119.     'one' and 'five' only occur in one statement each, 'two' and
  120.     'four' occur in two, and 'three' occurs in three statements:
  121.  
  122.       >>> len(list(t.search('one')))
  123.       1
  124.       >>> len(list(t.search('two')))
  125.       2
  126.       >>> len(list(t.search('three')))
  127.       3
  128.       >>> len(list(t.search('four')))
  129.       2
  130.       >>> len(list(t.search('five')))
  131.       1
  132.  
  133.     Lets add some more statements with different predicates.
  134.  
  135.       >>> e.add((URIRef('a'), URIRef('creator'), Literal('michel')))
  136.       >>> e.add((URIRef('b'), URIRef('creator'), Literal('Atilla the one Hun')))
  137.       >>> e.add((URIRef('c'), URIRef('creator'), Literal('michel')))
  138.       >>> e.add((URIRef('d'), URIRef('creator'), Literal('Hun Mung two')))
  139.  
  140.     Now 'one' occurs in two statements:
  141.  
  142.       >>> assert len(list(t.search('one'))) == 2
  143.  
  144.     And 'two' occurs in three statements, here they are:
  145.  
  146.       >>> t.search('two')
  147.       set([(rdflib.URIRef('d'), rdflib.URIRef('creator'), None), (rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  148.  
  149.     The predicates that are searched can be restricted by provding an
  150.     argument to 'search()':
  151.  
  152.       >>> t.search('two', URIRef('creator'))
  153.       set([(rdflib.URIRef('d'), rdflib.URIRef('creator'), None)])
  154.  
  155.       >>> t.search('two', URIRef(u'title'))
  156.       set([(rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  157.  
  158.     You can search for more than one term by simply including it in
  159.     the query:
  160.     
  161.       >>> t.search('two three', URIRef(u'title'))
  162.       set([(rdflib.URIRef('c'), rdflib.URIRef('title'), None), (rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  163.  
  164.     The above query returns all the statements that contain 'two' OR
  165.     'three'.  For the documents that contain 'two' AND 'three', do an
  166.     intersection of two queries:
  167.  
  168.       >>> t.search('two', URIRef(u'title')).intersection(t.search(u'three', URIRef(u'title')))
  169.       set([(rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  170.  
  171.     Intersection two queries like this is probably not the most
  172.     efficient way to do it, but for reasonable data sets this isn't a
  173.     problem.  Larger data sets will want to query the graph with
  174.     sparql or something else more efficient.
  175.  
  176.     In all the above queries, the object of each statement was always
  177.     'None'.  This is because the index graph does not store the object
  178.     data, that would make it very large, and besides the data is
  179.     available in the original data graph.  For convenience, a method
  180.     is provides to 'link' an index graph to a data graph.  This allows
  181.     the index to also provide object data in query results.
  182.  
  183.       >>> t.link_to(e)
  184.       >>> set([str(i[2]) for i in t.search('two', URIRef(u'title')).intersection(t.search(u'three', URIRef(u'title')))]) ==  set(['two three four', 'one two three'])
  185.       True
  186.  
  187.     You can remove the link by assigning None:
  188.  
  189.       >>> t.link_to(None)
  190.  
  191.     Unindexing means to remove statments from the index graph that
  192.     corespond to a statement in the data graph.  Note that while it is
  193.     possible to remove the index information of the occurances of
  194.     terms in statements, it is not possible to remove the terms
  195.     themselves, terms are 'absolute' and are never removed from the
  196.     index graph.  This is not a problem since languages have finite
  197.     terms:
  198.  
  199.       >>> e.remove((URIRef('a'), URIRef('creator'), Literal('michel')))
  200.       >>> e.remove((URIRef('b'), URIRef('creator'), Literal('Atilla the one Hun')))
  201.       >>> e.remove((URIRef('c'), URIRef('creator'), Literal('michel')))
  202.       >>> e.remove((URIRef('d'), URIRef('creator'), Literal('Hun Mung two')))
  203.  
  204.     Now 'one' only occurs in one statement:
  205.  
  206.       >>> assert len(list(t.search('one'))) == 1
  207.  
  208.     And 'two' only occurs in two statements, here they are:
  209.  
  210.       >>> t.search('two')
  211.       set([(rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  212.  
  213.     The predicates that are searched can be restricted by provding an
  214.     argument to 'search()':
  215.  
  216.       >>> t.search('two', URIRef(u'creator'))
  217.       set([])
  218.  
  219.       >>> t.search('two', URIRef(u'title'))
  220.       set([(rdflib.URIRef('a'), rdflib.URIRef('title'), None), (rdflib.URIRef('b'), rdflib.URIRef('title'), None)])
  221.  
  222.     """
  223.     linked_data = None
  224.     text_index = Namespace('http://rdflib.net/text_index#')
  225.     term = Namespace('http://rdflib.net/text_index#')['term']
  226.     termin = Namespace('http://rdflib.net/text_index#')['termin']
  227.     
  228.     def __init__(self, store = 'default'):
  229.         super(TextIndex, self).__init__(store)
  230.  
  231.     
  232.     def add_handler(self, event):
  233.         if type(event.triple[2]) is Literal:
  234.             self.index(event.triple)
  235.         
  236.  
  237.     
  238.     def remove_handler(self, event):
  239.         if type(event.triple[2]) is Literal:
  240.             self.unindex(event.triple)
  241.         
  242.  
  243.     
  244.     def index(self, .1):
  245.         (s, p, o) = .1
  246.         if type(o) is Literal:
  247.             for word in stopper(splitter(o)):
  248.                 word = Literal(word)
  249.                 if self.value(predicate = self.term, object = word, any = True):
  250.                     for t in set(self.triples((None, self.term, word))):
  251.                         t = t[0]
  252.                         if (t, self.termin, s) not in self:
  253.                             self.add((t, self.termin, s))
  254.                         
  255.                         if (p, t, s) not in self:
  256.                             self.add((p, t, s))
  257.                             continue
  258.                     
  259.                 h = md5(word)
  260.                 h.update(s)
  261.                 h.update(p)
  262.                 t = self.text_index['term_%s' % h.hexdigest()]
  263.                 self.add((t, self.term, word))
  264.                 self.add((t, self.termin, s))
  265.                 self.add((p, t, s))
  266.             
  267.         
  268.  
  269.     
  270.     def unindex(self, .1):
  271.         (s, p, o) = .1
  272.         if type(o) is Literal:
  273.             for word in stopper(splitter(o)):
  274.                 word = Literal(word)
  275.                 if self.value(predicate = self.term, object = word, any = True):
  276.                     for t in self.triples((None, self.term, word)):
  277.                         t = t[0]
  278.                         if (t, self.termin, s) in self:
  279.                             self.remove((t, self.termin, s))
  280.                         
  281.                         if (p, t, s) in self:
  282.                             self.remove((p, t, s))
  283.                             continue
  284.                     
  285.             
  286.         
  287.  
  288.     
  289.     def terms(self):
  290.         ''' Returns a generator that yields all of the term literals in the graph. '''
  291.         return set(self.objects(None, self.term))
  292.  
  293.     
  294.     def term_strings(self):
  295.         ''' Return a list of term strings. '''
  296.         return []([ str(i) for i in self.terms() ])
  297.  
  298.     
  299.     def search(self, terms, predicate = None):
  300.         ''' Returns a set of all the statements the term occurs in. '''
  301.         if predicate and not isinstance(predicate, URIRef):
  302.             _logger.warning('predicate is not a URIRef')
  303.             predicate = URIRef(predicate)
  304.         
  305.         results = set()
  306.         terms = [ Literal(term) for term in stopper(splitter(terms)) ]
  307.         for term in terms:
  308.             for t in self.triples((None, self.term, term)):
  309.                 for o in self.objects(t[0], self.termin):
  310.                     for p in self.triples((predicate, t[0], o)):
  311.                         if self.linked_data is None:
  312.                             results.add((o, p[0], None))
  313.                             continue
  314.                         []
  315.                         results.add((o, p[0], self.linked_data.value(o, p[0])))
  316.                     
  317.                 
  318.             
  319.         
  320.         return results
  321.  
  322.     
  323.     def index_graph(self, graph):
  324.         '''
  325.         Index a whole graph.  Must be a conjunctive graph.
  326.         '''
  327.         for t in graph.triples((None, None, None)):
  328.             self.index(t)
  329.         
  330.  
  331.     
  332.     def link_to(self, graph):
  333.         '''
  334.         Link to a graph
  335.         '''
  336.         self.linked_data = graph
  337.  
  338.     
  339.     def subscribe_to(self, graph):
  340.         '''
  341.         Subscribe this index to a graph.
  342.         '''
  343.         graph.store.dispatcher.subscribe(TripleAddedEvent, self.add_handler)
  344.         graph.store.dispatcher.subscribe(TripleRemovedEvent, self.remove_handler)
  345.  
  346.  
  347.  
  348. def test():
  349.     import doctest
  350.     doctest.testmod()
  351.  
  352. if __name__ == '__main__':
  353.     test()
  354.  
  355.