Skip to content

Commit 0baa1ba

Browse files
author
Richard Jones
committed
have RDBMS full-text indexer do AND searching [SF#1055435]
1 parent 18b3e65 commit 0baa1ba

File tree

6 files changed

+63
-11
lines changed

6 files changed

+63
-11
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Fixed:
7373
- fix indexer searching with no valid words (sf bug 1086787)
7474
- updated searching / indexing docs
7575
- fix "(list)" popup when list is one item long (sf bug 1064716)
76+
- have RDBMS full-text indexer do AND searching (sf bug 1055435)
7677

7778

7879
2004-10-26 0.7.9

roundup/backends/back_mysql.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ def db_exists(config):
105105
class Database(Database):
106106
arg = '%s'
107107

108+
# used by some code to switch styles of query
109+
implements_intersect = 0
110+
108111
# Backend for MySQL to use.
109112
# InnoDB is faster, but if you're running <4.0.16 then you'll need to
110113
# use BDB to pass all unit tests.

roundup/backends/back_postgresql.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def db_exists(config):
8181
class Database(rdbms_common.Database):
8282
arg = '%s'
8383

84+
# used by some code to switch styles of query
85+
implements_intersect = 1
86+
8487
def sql_open_connection(self):
8588
db = rdbms_common.connection_dict(self.config, 'database')
8689
self.config.logging.getLogger('hyperdb').info('open database %r'%(

roundup/backends/back_sqlite.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_sqlite.py,v 1.36.2.1 2004-12-14 23:23:35 richard Exp $
1+
# $Id: back_sqlite.py,v 1.36.2.2 2005-01-04 01:33:03 richard Exp $
22
'''Implements a backend for SQLite.
33
44
See https://pysqlite.sourceforge.net/ for pysqlite info
@@ -25,6 +25,10 @@ def db_nuke(config):
2525
class Database(rdbms_common.Database):
2626
# char to use for positional arguments
2727
arg = '%s'
28+
29+
# used by some code to switch styles of query
30+
implements_intersect = 1
31+
2832
hyperdb_to_sql_datatypes = {
2933
hyperdb.String : 'VARCHAR(255)',
3034
hyperdb.Date : 'VARCHAR(30)',

roundup/backends/indexer_rdbms.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,53 @@ def find(self, wordlist):
7979

8080
l = [word.upper() for word in wordlist if 26 > len(word) > 2]
8181

82-
a = ','.join([self.db.arg] * len(l))
83-
sql = 'select distinct(_textid) from __words where _word in (%s)'%a
84-
self.db.cursor.execute(sql, tuple(l))
85-
r = self.db.cursor.fetchall()
86-
if not r:
82+
if not l:
8783
return {}
88-
a = ','.join([self.db.arg] * len(r))
89-
sql = 'select _class, _itemid, _prop from __textids '\
90-
'where _textid in (%s)'%a
91-
self.db.cursor.execute(sql, tuple([int(id) for (id,) in r]))
84+
85+
if self.db.implements_intersect:
86+
# simple AND search
87+
sql = 'select distinct(_textid) from __words where _word=%s'%self.db.arg
88+
sql = '\nINTERSECT\n'.join([sql]*len(l))
89+
self.db.cursor.execute(sql, tuple(l))
90+
r = self.db.cursor.fetchall()
91+
if not r:
92+
return {}
93+
a = ','.join([self.db.arg] * len(r))
94+
sql = 'select _class, _itemid, _prop from __textids '\
95+
'where _textid in (%s)'%a
96+
self.db.cursor.execute(sql, tuple([int(id) for (id,) in r]))
97+
98+
else:
99+
# A more complex version for MySQL since it doesn't implement INTERSECT
100+
101+
# Construct SQL statement to join __words table to itself
102+
# multiple times.
103+
sql = """select distinct(__words1._textid)
104+
from __words as __words1 %s
105+
where __words1._word=%s %s"""
106+
107+
join_tmpl = ' left join __words as __words%d using (_textid) \n'
108+
match_tmpl = ' and __words%d._word=%s \n'
109+
110+
join_list = []
111+
match_list = []
112+
for n in xrange(len(l) - 1):
113+
join_list.append(join_tmpl % (n + 2))
114+
match_list.append(match_tmpl % (n + 2, self.db.arg))
115+
116+
sql = sql%(' '.join(join_list), self.db.arg, ' '.join(match_list))
117+
self.db.cursor.execute(sql, l)
118+
119+
r = map(lambda x: x[0], self.db.cursor.fetchall())
120+
if not r:
121+
return {}
122+
123+
a = ','.join([self.db.arg] * len(r))
124+
sql = 'select _class, _itemid, _prop from __textids '\
125+
'where _textid in (%s)'%a
126+
127+
self.db.cursor.execute(sql, tuple(map(int, r)))
128+
92129
# self.search_index has the results as {some id: identifier} ...
93130
# sigh
94131
r = {}

test/db_test_base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: db_test_base.py,v 1.55.2.1 2005-01-03 03:23:38 richard Exp $
18+
# $Id: db_test_base.py,v 1.55.2.2 2005-01-04 01:33:04 richard Exp $
1919

2020
import unittest, os, shutil, errno, imp, sys, time, pprint
2121

@@ -707,6 +707,10 @@ def testIndexerSearching(self):
707707
self.assertEquals(self.db.indexer.search(['flebble'], self.db.issue),
708708
{i1: {}, i2: {}})
709709

710+
# test AND'ing of search terms
711+
self.assertEquals(self.db.indexer.search(['frooz', 'flebble'],
712+
self.db.issue), {i2: {}})
713+
710714
# unindexed stopword
711715
self.assertEquals(self.db.indexer.search(['the'], self.db.issue), {})
712716

0 commit comments

Comments
 (0)