Plugin dev: QUERY with OR

The below code works works correctly matching any item with bpm=0 OR with gender not set. …but it looks very ugly :nauseated_face:

query = "bpm:0 , gender::^$"
library_items = self.lib.items(query)

Is there a better or more elegant way to do this?

That will certainly work! But you can also manually construct a query object instead of relying on the parser. Like in this test:

1 Like

Thanks! This is very neat. However, one of the fields(gender) I am trying to use is not a field but a flex attribute.

q = dbcore.query.OrQuery(
            [
                dbcore.query.NumericQuery(u'bpm', u'0'),
                dbcore.query.MatchQuery(u'gender', u'',),
                #dbcore.query.NoneQuery(u'gender')
            ]
        )

in fact I get:

sqlite3.OperationalError: no such column: gender

I cannot see any tests either regarding flexible attributes.

Try passing fast=False to the MatchQuery constructor, which will make it work for flexible attributes.

That works well, thanks.
My final query ended up being this:

q = dbcore.query.OrQuery(
            [
                dbcore.query.NumericQuery(u'bpm', u'0'),
                dbcore.query.MatchQuery(u'gender', u'', fast=False),
                dbcore.query.MatchQuery(u'gender', None, fast=False)
            ]
        )

It works as expected, however, I am not entirely sure about the dbcore.query.MatchQuery(u'gender', None, fast=False) part.
I tried with the NoneQuery by adding dbcore.query.NoneQuery(u'gender', fast=False)but it failed with:

AttributeError: type object ‘NoneQuery’ has no attribute ‘field’

which was not at all clear to me.
Is there a better way to match an unset flexible attribute or is the above fine?

Hmm; I’m confused about the NoneQuery error—can you please include the full traceback? (The above should work just as well, so no need to change, but I’d be interested to see why that happens.)

Sure, here it comes:

(beetsdevel) jakabimac:extractors jackisback$ beet -v xtractor -c
user configuration: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/BEETSDIR/config.yaml
data directory: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/BEETSDIR
plugin paths: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/beetsplug
Sending event: pluginload
library database: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/BEETSDIR/library.db
library directory: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/BEETSDIR/Music
Sending event: library_opened
xtractor: Combined query: AndQuery([AndQuery([TrueQuery()]), OrQuery([NumericQuery('bpm', '0', True), MatchQuery('gender', '', False), NoneQuery('gender', False)])])
Traceback (most recent call last):
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/bin/beet", line 8, in <module>
    sys.exit(main())
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/ui/__init__.py", line 1266, in main
    _raw_main(args)
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/ui/__init__.py", line 1253, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/beetsplug/xtractor/command.py", line 133, in func
    self.xtract()
  File "/Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/beetsplug/xtractor/command.py", line 157, in xtract
    if len(library_items) == 0:
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/db.py", line 741, in __len__
    for obj in self:
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/db.py", line 689, in _get_objects
    if not self.query or self.query.match(obj):
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/query.py", line 456, in match
    return all([q.match(item) for q in self.subqueries])
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/query.py", line 456, in <listcomp>
    return all([q.match(item) for q in self.subqueries])
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/query.py", line 466, in match
    return any([q.match(item) for q in self.subqueries])
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/query.py", line 466, in <listcomp>
    return any([q.match(item) for q in self.subqueries])
  File "/Users/jackisback/opt/miniconda3/envs/beetsdevel/lib/python3.7/site-packages/beets/dbcore/query.py", line 162, in match
    return item[cls.field] is None
AttributeError: type object 'NoneQuery' has no attribute 'field'

(beetsdevel) jakabimac:extractors jackisback$ beet version
beets version 1.4.9
Python version 3.7.6
plugins: info, xtractor

Huh, that looks like a bug to me. I think NotQuery.match should probably not be a @classmethod:

I think this bug has been there since NoneQuery was originally introduced 6 years ago? Wow. I guess we should fix that—if you have a chance, would you mind filing a bug?

Issue created: https://github.com/beetbox/beets/issues/3516
It looks relatively simple to fix. It should just simply be an instance method, no?

1 Like