Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sorting search result by health #5880

Merged
merged 3 commits into from
Dec 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from asyncio import get_event_loop

from pony import orm
from pony.orm import db_session, desc, raw_sql, select
from pony.orm import db_session, desc, left_join, raw_sql

from tribler_core.modules.metadata_store.orm_bindings.channel_node import LEGACY_ENTRY, TODELETE
from tribler_core.modules.metadata_store.orm_bindings.torrent_metadata import NULL_KEY_SUBST
Expand Down Expand Up @@ -52,7 +52,7 @@ def search_keyword(cls, query, lim=100):
"""SELECT rowid FROM ChannelNode WHERE rowid IN (SELECT rowid FROM FtsIndex WHERE FtsIndex MATCH $query
ORDER BY bm25(FtsIndex) LIMIT $lim) GROUP BY infohash"""
)
return cls.select(lambda g: g.rowid in fts_ids)
return left_join(g for g in cls if g.rowid in fts_ids) # pylint: disable=E1135

@classmethod
@db_session
Expand Down Expand Up @@ -81,7 +81,7 @@ def get_entries_query(
"""
# Warning! For Pony magic to work, iteration variable name (e.g. 'g') should be the same everywhere!

pony_query = cls.search_keyword(txt_filter, lim=1000) if txt_filter else select(g for g in cls)
pony_query = cls.search_keyword(txt_filter, lim=1000) if txt_filter else left_join(g for g in cls)

if metadata_type is not None:
try:
Expand Down Expand Up @@ -120,11 +120,13 @@ def get_entries_query(
)

# Sort the query
pony_query = pony_query.sort_by("desc(g.rowid)" if sort_desc else "g.rowid")

if sort_by == "HEALTH":
pony_query = (
pony_query.sort_by("(desc(g.health.seeders), desc(g.health.leechers))")
pony_query = pony_query.sort_by(
"(desc(g.health.seeders), desc(g.health.leechers))"
if sort_desc
else pony_query.sort_by("(g.health.seeders, g.health.leechers)")
else "(g.health.seeders, g.health.leechers)"
)
elif sort_by == "size" and not issubclass(cls, db.ChannelMetadata):
# TODO: optimize this check to skip cases where size field does not matter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async def test_get_channels(enable_chant, enable_api, add_fake_torrents_channels
json_dict = await do_request(session, 'channels')
assert len(json_dict['results']) == 10
# Default channel state should be METAINFO_LOOKUP
assert json_dict['results'][0]['state'] == CHANNEL_STATE.METAINFO_LOOKUP.value
assert json_dict['results'][-1]['state'] == CHANNEL_STATE.METAINFO_LOOKUP.value

# We test out different combinations of channels' states and download progress
# State UPDATING:
Expand All @@ -38,7 +38,7 @@ async def test_get_channels(enable_chant, enable_api, add_fake_torrents_channels
channel.local_version = 123

json_dict = await do_request(session, 'channels')
assert json_dict['results'][0]['progress'] == 0.5
assert json_dict['results'][-1]['progress'] == 0.5

# State DOWNLOADING
with db_session:
Expand All @@ -49,7 +49,7 @@ async def test_get_channels(enable_chant, enable_api, add_fake_torrents_channels
session.dlmgr.metainfo_requests.get = lambda _: False
session.dlmgr.download_exists = lambda _: True
json_dict = await do_request(session, 'channels')
assert json_dict['results'][0]['state'] == CHANNEL_STATE.DOWNLOADING.value
assert json_dict['results'][-1]['state'] == CHANNEL_STATE.DOWNLOADING.value


@pytest.mark.asyncio
Expand Down Expand Up @@ -122,7 +122,6 @@ async def test_get_channel_contents(enable_chant, enable_api, add_fake_torrents_
with db_session:
chan = session.mds.ChannelMetadata.select().first()
json_dict = await do_request(session, 'channels/%s/123' % hexlify(chan.public_key), expected_code=200)
print(json_dict)
assert len(json_dict['results']) == 5
assert 'status' in json_dict['results'][0]
assert json_dict['results'][0]['progress'] == 0.5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,132 @@ def test_get_channels(metadata_store):
assert len(channels) == 5


@db_session
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Wow! Quite a test 👏

def test_sort_by_health(metadata_store):
def save():
metadata_store._db.flush() # pylint: disable=W0212

channel = metadata_store.ChannelMetadata.create_channel('channel1')
save()

torrent1 = metadata_store.TorrentMetadata(
origin_id=channel.id_, status=NEW, infohash=random_infohash(), title='torrent1 aaa bbb'
)
torrent1.health.set(seeders=10, leechers=20)
save()

folder1 = metadata_store.CollectionNode(origin_id=channel.id_, title='folder1 aaa ccc')
save()

torrent2 = metadata_store.TorrentMetadata(
origin_id=channel.id_, status=NEW, infohash=random_infohash(), title='torrent2 bbb ccc'
)
torrent2.health.set(seeders=5, leechers=10)
save()

folder2 = metadata_store.CollectionNode(origin_id=channel.id_, title='folder2 aaa bbb')
save()

torrent3 = metadata_store.TorrentMetadata(
origin_id=channel.id_, status=NEW, infohash=random_infohash(), title='torrent3 ccc ddd'
)
torrent3.health.set(seeders=30, leechers=40)
save()

folder2_1 = metadata_store.CollectionNode(
origin_id=folder2.id_,
title='folder2_1 aaa bbb',
)
save()

folder2_2 = metadata_store.CollectionNode(
origin_id=folder2.id_,
title='folder2_2 bbb ccc',
)
save()

torrent2_1 = metadata_store.TorrentMetadata(
origin_id=folder2.id_, status=NEW, infohash=random_infohash(), title='torrent2_1 aaa ccc'
)
torrent2_1.health.set(seeders=20, leechers=10)
save()

# Without FTS search

objects = metadata_store.MetadataNode.get_entries(channel_pk=channel.public_key, sort_by='HEALTH', sort_desc=True)
titles = [obj.title.partition(' ')[0] for obj in objects]
assert titles == [
'torrent3',
'torrent2_1',
'torrent1',
'torrent2',
'channel1',
'folder2_2',
'folder2_1',
'folder2',
'folder1',
]

objects = metadata_store.MetadataNode.get_entries(channel_pk=channel.public_key, sort_by='HEALTH', sort_desc=False)
titles = [obj.title.partition(' ')[0] for obj in objects]
assert titles == [
'folder1',
'folder2',
'folder2_1',
'folder2_2',
'channel1',
'torrent2',
'torrent1',
'torrent2_1',
'torrent3',
]

objects = metadata_store.MetadataNode.get_entries(
origin_id=channel.id_,
sort_by='HEALTH',
sort_desc=True,
)
titles = [obj.title.partition(' ')[0] for obj in objects]
assert titles == ['torrent3', 'torrent1', 'torrent2', 'folder2', 'folder1']

objects = metadata_store.MetadataNode.get_entries(origin_id=channel.id_, sort_by='HEALTH', sort_desc=False)
titles = [obj.title.partition(' ')[0] for obj in objects]
assert titles == ['folder1', 'folder2', 'torrent2', 'torrent1', 'torrent3']

# With FTS search

objects = metadata_store.MetadataNode.get_entries(
channel_pk=channel.public_key, txt_filter='aaa', sort_by='HEALTH', sort_desc=True
)
titles = [obj.title.partition(' ')[0] for obj in objects]
# FIXME: does not return folder2 and folder2_1 due to a bug in a full text search query
assert titles == ['torrent2_1', 'torrent1', 'folder1']

objects = metadata_store.MetadataNode.get_entries(
channel_pk=channel.public_key, txt_filter='aaa', sort_by='HEALTH', sort_desc=False
)
titles = [obj.title.partition(' ')[0] for obj in objects]
# FIXME: does not return folder2 and folder2_1 due to a bug in a full text search query
assert titles == ['folder1', 'torrent1', 'torrent2_1']

objects = metadata_store.MetadataNode.get_entries(
origin_id=channel.id_,
txt_filter='aaa',
sort_by='HEALTH',
sort_desc=True,
)
titles = [obj.title.partition(' ')[0] for obj in objects]
# FIXME: does not return folder2 due to a bug in a full text search query
assert titles == ['torrent1', 'folder1']

objects = metadata_store.MetadataNode.get_entries(
origin_id=channel.id_, txt_filter='aaa', sort_by='HEALTH', sort_desc=False
)
titles = [obj.title.partition(' ')[0] for obj in objects]
# FIXME: does not return folder2 due to a bug in a full text search query
assert titles == ['folder1', 'torrent1']


@db_session
def test_get_channel_name(metadata_store):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def test_get_entries(metadata_store):

args = dict(channel_pk=channel_pk, hide_xxx=True, exclude_deleted=True, metadata_type=REGULAR_TORRENT)
torrents = metadata_store.TorrentMetadata.get_entries_query(**args)[:]
assert tlist[-5:-2] == list(torrents)
assert tlist[-5:-2] == list(torrents)[::-1]

count = metadata_store.TorrentMetadata.get_entries_count(**args)
assert count == 3
Expand Down