Skip to content
Open
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
99 changes: 99 additions & 0 deletions hydrus/client/db/ClientDBFilesSearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2430,7 +2430,106 @@ def _DoNotePreds( self, system_predicates: ClientSearchFileSearchContext.FileSys
notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteName( note_name, temp_table_name, job_status = job_status )

query_hash_ids.difference_update( notes_hash_ids )



if 'note_matches' in simple_preds:

inclusive_contents = simple_preds[ 'note_matches' ]

for content in inclusive_contents:

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteContent( content, temp_table_name, job_status = job_status )

query_hash_ids = intersection_update_qhi( query_hash_ids, notes_hash_ids )



if 'not_note_matches' in simple_preds:

exclusive_contents = simple_preds[ 'not_note_matches' ]

for content in exclusive_contents:

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteContent( content, temp_table_name, job_status = job_status )

query_hash_ids.difference_update( notes_hash_ids )



if 'note_contains' in simple_preds:

inclusive_contents = simple_preds[ 'note_contains' ]

for content in inclusive_contents:

content = f"%{content}%"

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteContent( content, temp_table_name, job_status = job_status )

query_hash_ids = intersection_update_qhi( query_hash_ids, notes_hash_ids )



if 'not_note_contains' in simple_preds:

exclusive_contents = simple_preds[ 'not_note_contains' ]

for content in exclusive_contents:

content = f"%{content}%"

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteContent( content, temp_table_name, job_status = job_status )

query_hash_ids.difference_update( notes_hash_ids )



if 'note_contains_words' in simple_preds:

inclusive_contents = simple_preds[ 'note_contains_words' ]

for content in inclusive_contents:

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteWords( content, temp_table_name, job_status = job_status )

query_hash_ids = intersection_update_qhi( query_hash_ids, notes_hash_ids )



if 'not_note_contains_words' in simple_preds:

exclusive_contents = simple_preds[ 'not_note_contains_words' ]

for content in exclusive_contents:

with self._MakeTemporaryIntegerTable( query_hash_ids, 'hash_id' ) as temp_table_name:

self._AnalyzeTempTable( temp_table_name )

notes_hash_ids = self.modules_notes_map.GetHashIdsFromNoteWords( content, temp_table_name, job_status = job_status )

query_hash_ids.difference_update( notes_hash_ids )



Expand Down
31 changes: 30 additions & 1 deletion hydrus/client/db/ClientDBNotesMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,36 @@ def GetHashIdsFromNoteName( self, name: str, hash_ids_table_name: str, job_statu
return self._STS( self._ExecuteCancellable( 'SELECT hash_id FROM file_notes CROSS JOIN {} USING ( hash_id ) WHERE name_id = ?;'.format( hash_ids_table_name ), ( label_id, ), cancelled_hook ) )


def GetHashIdsFromNumNotes( self, number_tests: list[ ClientNumberTest.NumberTest ], hash_ids: collections.abc.Collection[ int ], hash_ids_table_name: str, job_status: ClientThreading.JobStatus | None = None ):

def GetHashIdsFromNoteContent( self, content_pattern: str, hash_ids_table_name: str, job_status: typing.Optional[ ClientThreading.JobStatus ] = None ):

cancelled_hook = None

if job_status is not None:

cancelled_hook = job_status.IsCancelled

return self._STS( self._ExecuteCancellable(
fr"SELECT hash_id FROM {hash_ids_table_name} CROSS JOIN file_notes USING ( hash_id ) CROSS JOIN notes using ( note_id ) WHERE note LIKE ? ESCAPE '\';", ( content_pattern, ), cancelled_hook) )


def GetHashIdsFromNoteWords( self, words: str, hash_ids_table_name: str, job_status: typing.Optional[ ClientThreading.JobStatus ] = None ):

cancelled_hook = None

if job_status is not None:

cancelled_hook = job_status.IsCancelled

words = [f"%{word}%" for word in words.split(" ")]

query = " AND ".join([ fr"note LIKE ? ESCAPE '\'" for _ in words ])

return self._STS( self._ExecuteCancellable(
fr"SELECT hash_id FROM {hash_ids_table_name} CROSS JOIN file_notes USING ( hash_id ) CROSS JOIN notes using ( note_id ) WHERE {query};", words, cancelled_hook) )


def GetHashIdsFromNumNotes( self, number_tests: list[ ClientNumberTest.NumberTest ], hash_ids: collections.abc.Collection[ int ], hash_ids_table_name: str, job_status: typing.Optional[ ClientThreading.JobStatus ] = None ):

result_hash_ids = set( hash_ids )

Expand Down
71 changes: 71 additions & 0 deletions hydrus/client/gui/search/ClientGUIPredicatesSingle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,77 @@ def GetPredicates( self ):
return predicates



class PanelPredicateSystemNoteContent( PanelPredicateSystemSingle ):

def __init__( self, parent, predicate ):

super().__init__( parent )

self._operator = ClientGUICommon.BetterChoice( self )

self._operator.addItem( 'has note that contains', (0, True) )
self._operator.addItem( 'does not have note that contains', (0, False) )
self._operator.addItem( 'has note that contains the words', (1, True) )
self._operator.addItem( 'does not have note that contains the words', (1, False) )
self._operator.addItem( 'has note that matches', (2, True) )
self._operator.addItem( 'does not have note that matches', (2, False) )

self._name = QW.QLineEdit( self )

#

predicate = self._GetPredicateToInitialisePanelWith( predicate )

( operator, name ) = predicate.GetValue()

self._operator.SetValue( operator )
self._name.setText( name )

#

hbox = QP.HBoxLayout()

QP.AddToLayout( hbox, ClientGUICommon.BetterStaticText(self,'system:note content'), CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._operator, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._name, CC.FLAGS_CENTER_PERPENDICULAR )

hbox.addStretch( 0 )

self.setLayout( hbox )

self.setFocusProxy( self._operator )


def GetDefaultPredicate( self ):

operator = True
name = ''

return ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT, ( operator, name ) )


def GetPredicates( self ):

name = self._name.text()

if name == '':

name = 'notes'

index, operator = self._operator.GetValue()

types = [
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT
]

predicates = ( ClientSearchPredicate.Predicate( types[index], ( operator, name ) ), )

return predicates


class PanelPredicateSystemHeight( PanelPredicateSystemSingle ):

def __init__( self, parent, predicate ):
Expand Down
17 changes: 16 additions & 1 deletion hydrus/client/gui/search/ClientGUISearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ def __init__( self, parent, predicates: collections.abc.Collection[ ClientSearch

self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName( self, predicate ) )

elif predicate_type in (
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS,
):

self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNoteContent( self, predicate ) )

elif predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_WORDS:

self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumWords( self, predicate ) )
Expand Down Expand Up @@ -770,13 +778,20 @@ def __init__( self, parent, predicate: ClientSearchPredicate.Predicate ):

elif predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTES:

recent_predicate_types = [ ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME ]
recent_predicate_types = [
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_NOTES,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT,
ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS,
]

static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ClientNumberTest.NumberTest.STATICCreateFromCharacters( '>', 0 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ClientNumberTest.NumberTest.STATICCreateFromCharacters( '=', 0 ) ), ), show_remove_button = False ) )

editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumNotes, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNoteContent, predicate ) )

elif predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NUM_WORDS:

Expand Down
62 changes: 62 additions & 0 deletions hydrus/client/search/ClientSearchFileSearchContext.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,68 @@ def __init__( self, predicates: collections.abc.Collection[ ClientSearchPredicat
self._common_info[ label ].add( name )


if predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT:

( operator, name ) = value

if operator:

label = 'note_matches'

else:

label = 'not_note_matches'


if label not in self._common_info:

self._common_info[ label ] = set()


self._common_info[ label ].add( name )


if predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT:

( operator, name ) = value

if operator:

label = 'note_contains'

else:

label = 'not_note_contains'


if label not in self._common_info:

self._common_info[ label ] = set()


self._common_info[ label ].add( name )


if predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS:

( operator, name ) = value

if operator:

label = 'note_contains_words'

else:

label = 'not_note_contains_words'


if label not in self._common_info:

self._common_info[ label ] = set()


self._common_info[ label ].add( name )

if predicate_type == ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_LIMIT:

limit = value
Expand Down
6 changes: 6 additions & 0 deletions hydrus/client/search/ClientSearchParseSystemPredicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ def viewtime_pred_generator( o, v, u ):
SystemPredicateParser.Predicate.TAG_ADVANCED_INCLUSIVE : lambda o, v, u: ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_TAG_ADVANCED, ( o[0], o[1], o[2], v ) ),
SystemPredicateParser.Predicate.TAG_ADVANCED_EXCLUSIVE : lambda o, v, u: ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_TAG_ADVANCED, ( o[0], o[1], o[2], v ), inclusive = False ),
SystemPredicateParser.Predicate.RATING_ADVANCED : lambda o, v, u: ClientSearchPredicate.Predicate( ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_RATING_ADVANCED, v ),
SystemPredicateParser.Predicate.MATCHES_NOTE_CONTENT : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT, ( True, strip_quotes( v ) ) ),
SystemPredicateParser.Predicate.NO_MATCHES_NOTE_CONTENT : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_MATCHES_CONTENT, ( False, strip_quotes( v ) ) ),
SystemPredicateParser.Predicate.CONTAINS_NOTE_WORDS : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS, ( True, strip_quotes( v ) ) ),
SystemPredicateParser.Predicate.NO_CONTAINS_NOTE_WORDS : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_WORDS, ( False, strip_quotes( v ) ) ),
SystemPredicateParser.Predicate.CONTAINS_NOTE_CONTENT : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT, ( True, strip_quotes( v ) ) ),
SystemPredicateParser.Predicate.NO_CONTAINS_NOTE_CONTENT : lambda o, v, u: ClientSearchPredicate.Predicate(ClientSearchPredicate.PREDICATE_TYPE_SYSTEM_NOTE_CONTAINS_CONTENT, ( False, strip_quotes( v ) ) ),
}

def ParseSystemPredicateStringsToPredicates( system_predicate_strings: collections.abc.Collection[ str ], discard_failures = False ) -> list[ ClientSearchPredicate.Predicate ]:
Expand Down
Loading