new search interface, support negating tags, use tag: not t:

This commit is contained in:
Damien Elmes 2009-04-06 11:41:50 +09:00
parent 62375e37a3
commit 36825006d0
3 changed files with 146 additions and 14 deletions

View file

@ -47,7 +47,7 @@ REV_CARDS_NEW_FIRST = 1
REV_CARDS_DUE_FIRST = 2
REV_CARDS_RANDOM = 3
DECK_VERSION = 31
DECK_VERSION = 32
# parts of the code assume we only have one deck
decksTable = Table(
@ -1661,6 +1661,85 @@ where id = :id""", pending)
self.finishProgress()
self.refresh()
# Find
##########################################################################
def _parseQuery(self, query):
tokens = []
res = []
# break query into words or phrases
for match in re.findall('"(.+?)"|([^ "]+)', query):
tokens.append(match[0] or match[1])
for c, token in enumerate(tokens):
isNeg = token.startswith("-")
if isNeg:
token = token[1:]
isTag = token.startswith("tag:")
if isTag:
# tag
token = token[4:]
res.append((token, isNeg, isTag))
return res
def _findCards(self, query):
"Find facts matching QUERY."
tquery = ""
fquery = ""
args = {}
for c, (token, isNeg, isTag) in enumerate(self._parseQuery(query)):
if isTag:
# a tag
if tquery:
if isNeg:
tquery += " except "
else:
tquery += " intersect "
elif isNeg:
tquery += "select id from cards except "
if token == "none":
tquery += """
select cards.id from cards, facts where facts.tags = ''
and cards.factId = facts.id """
else:
token = token.replace("*", "%")
ids = self.s.column0(
"select id from tags where tag like :tag", tag=token)
tquery += """
select cardId from cardTags where
cardTags.tagId in %s""" % ids2str(ids)
else:
# a field
if fquery:
if isNeg:
fquery += " except "
else:
fquery += " intersect "
elif isNeg:
fquery += "select id from facts except "
args["_ff_%d" % c] = "%"+token+"%"
q = "select factId from fields where value like :_ff_%d" % c
fquery += q
return (tquery, fquery, args)
def findCardsWhere(self, query):
(tquery, fquery, args) = self._findCards(query)
q = ""
x = []
if tquery:
x.append(" id in (%s)" % tquery)
if fquery:
x.append(" factId in (%s)" % fquery)
if x:
q += " and ".join(x)
return q, args
def findCards(self, query):
q, args = self.findCardsWhere(query)
query = "select id from cards"
if q:
query += " where " + q
return self.s.column0(query, **args)
# Find and replace
##########################################################################
@ -2460,9 +2539,9 @@ create index if not exists ix_mediaDeleted_factId on mediaDeleted (mediaId)""")
deck.s.statement("""
create index if not exists ix_tags_tag on tags (tag)""")
deck.s.statement("""
create index if not exists ix_cardTags_cardId on cardTags (cardId)""")
create index if not exists ix_cardTags_tagCard on cardTags (tagId, cardId)""")
deck.s.statement("""
create index if not exists ix_cardTags_tagId on cardTags (tagId)""")
create index if not exists ix_cardTags_cardId on cardTags (cardId)""")
_addIndices = staticmethod(_addIndices)
def _addViews(deck):
@ -2832,6 +2911,13 @@ nextFactor, reps, thinkingTime, yesCount, noCount from reviewHistory""")
deck.version = 31
deck.s.commit()
deck.s.statement("vacuum")
if deck.version < 32:
deck.s.execute("drop index if exists ix_cardTags_tagId")
deck.s.execute("drop index if exists ix_cardTags_cardId")
DeckStorage._addIndices(deck)
deck.s.execute("analyze")
deck.version = 32
deck.s.commit()
# this check we do regardless of version number since doing it on init
# seems to crash
if (deck.s.scalar("pragma page_size") == 1024 or

View file

@ -21,18 +21,18 @@ from anki.db import *
def initTagTables(s):
try:
s.statement("""
create table tags (
id integer not null,
tag text not null collate nocase,
priority integer not null default 2,
primary key(id))""")
create table tags (
id integer not null,
tag text not null collate nocase,
priority integer not null default 2,
primary key(id))""")
s.statement("""
create table cardTags (
id integer not null,
cardId integer not null,
tagId integer not null,
src integer not null,
primary key(id))""")
create table cardTags (
id integer not null,
cardId integer not null,
tagId integer not null,
src integer not null,
primary key(id))""")
except:
pass

View file

@ -211,3 +211,49 @@ select question, answer from cards where factId = :id""",
id=f.id)
assert stripHTML(q) == u"e"
assert stripHTML(a) == u"r"
def test_findCards():
deck = DeckStorage.Deck()
deck.addModel(BasicModel())
f = deck.newFact()
f['Front'] = u'dog'
f['Back'] = u'cat'
f.tags = u"monkey"
deck.addFact(f)
f = deck.newFact()
f['Front'] = u'goats are fun'
f['Back'] = u'sheep'
f.tags = u"sheep goat horse"
deck.addFact(f)
f = deck.newFact()
f['Front'] = u'cat'
f['Back'] = u'sheep'
deck.addFact(f)
assert not deck.findCards("tag:donkey")
assert len(deck.findCards("tag:sheep")) == 1
assert len(deck.findCards("tag:sheep tag:goat")) == 1
assert len(deck.findCards("tag:sheep tag:monkey")) == 0
assert len(deck.findCards("tag:monkey")) == 1
assert len(deck.findCards("tag:sheep -tag:monkey")) == 1
assert len(deck.findCards("-tag:sheep")) == 2
assert len(deck.findCards("cat")) == 2
assert len(deck.findCards("cat -dog")) == 1
assert len(deck.findCards("cat -dog")) == 1
assert len(deck.findCards("are goats")) == 1
assert len(deck.findCards('"are goats"')) == 0
assert len(deck.findCards('"goats are"')) == 1
# make sure card templates and models match too
assert len(deck.findCards('tag:basic')) == 3
assert len(deck.findCards('tag:forward')) == 3
deck.addModel(JapaneseModel())
f = deck.newFact()
f['Expression'] = u'foo'
f['Meaning'] = u'bar'
deck.addFact(f)
deck.currentModel.cardModels[1].active = True
f = deck.newFact()
f['Expression'] = u'baz'
f['Meaning'] = u'qux'
c = deck.addFact(f)
assert len(deck.findCards('tag:recognition')) == 2
assert len(deck.findCards('tag:recall')) == 1