mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
new search interface, support negating tags, use tag: not t:
This commit is contained in:
parent
62375e37a3
commit
36825006d0
3 changed files with 146 additions and 14 deletions
92
anki/deck.py
92
anki/deck.py
|
@ -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
|
||||
|
|
22
anki/tags.py
22
anki/tags.py
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue