Ion Androutsopoulos

187 papers A* 9A 5B 5C 2Journal 85Unranked 77
YearRankTypeTitle / Venue / Authors
2025 conf
CLEF (Working Notes)
Anna Chatzipapadopoulou, Ippokratis Pantelidis, Foivos Charalampakos, Marina Samprovalaki, Georgios Moschovis, Panagiotis Kaliosis, Kalliopi V. Dalakleidi, John Pavlopoulos, Ion Androutsopoulos
2025 conf
COLING Workshops
Ioannis Chasandras, Odysseas S. Chlapanis, Ion Androutsopoulos
2025 J jnl
CoRR
Christos Vlachos, Nikolaos Stylianou, Alexandra Fiotaki, Spiros Methenitis, Elisavet Palogiannidi, Themos Stafylakis, Ion Androutsopoulos
2025 conf
WWW (Companion Volume)
Lefteris Loukas, Fabian Billert, Manos Fergadiotis, Prodromos Malakasiotis, Ion Androutsopoulos
2025 A* conf
EMNLP
Katerina Korre, Dimitris Tsirmpas, Nikos Gkoumas, Emma Cabalé, Danai Myrtzani, Theodoros Evgeniou, Ion Androutsopoulos, John Pavlopoulos
2025 J jnl
CoRR
Katerina Korre, Dimitris Tsirmpas, Nikos Gkoumas, Emma Cabalé, Dionysis Kontarinis, Danai Myrtzani, Theodoros Evgeniou, Ion Androutsopoulos, John Pavlopoulos
2025 conf
COLING (Demonstrations)
Lefteris Loukas, Nikolaos Smyrnioudis, Chrysa Dikonomaki, Spyros Barbakos, Anastasios Toumazatos, John Koutsikakis, Manolis Kyriakakis, Mary Georgiou, Stavros Vassos, John Pavlopoulos, Ion Androutsopoulos
2025 conf
EMNLP (Findings)
Odysseas S. Chlapanis, Dimitris Galanis, Nikolaos Aletras, Ion Androutsopoulos
2025 J jnl
CoRR
Odysseas S. Chlapanis, Dimitrios Galanis, Nikolaos Aletras, Ion Androutsopoulos
2025 conf
ACL (1)
Nikolaos Nikolaidis, Nicolas Stefanovitch, Purificação Silvano, Dimitar Iliyanov Dimitrov, Roman Yangarber, Nuno Guimarães, Elisa Sartori, Ion Androutsopoulos, Preslav Nakov, Giovanni Da San Martino, Jakub Piskorski
2025 J jnl
CoRR
Dimitris Tsirmpas, Ion Androutsopoulos, John Pavlopoulos
2024 conf
ACL (Findings)
Panagiotis Kaliosis, John Pavlopoulos, Foivos Charalampakos, Georgios Moschovis, Ion Androutsopoulos
2024 J jnl
CoRR
Panagiotis Kaliosis, John Pavlopoulos, Foivos Charalampakos, Georgios Moschovis, Ion Androutsopoulos
2024 conf
CLEF (Working Notes)
Marina Samprovalaki, Anna Chatzipapadopoulou, Georgios Moschovis, Foivos Charalampakos, Panagiotis Kaliosis, John Pavlopoulos, Ion Androutsopoulos
2024 J jnl
CoRR
Ioannis Chasandras, Odysseas S. Chlapanis, Ion Androutsopoulos
2024 conf
SemEval@NAACL
Odysseas S. Chlapanis, Ion Androutsopoulos, Dimitrios Galanis
2024 J jnl
CoRR
Odysseas S. Chlapanis, Ion Androutsopoulos, Dimitrios Galanis
2024 conf
ACL (Findings)
Christos Vlachos, Themos Stafylakis, Ion Androutsopoulos
2024 J jnl
CoRR
Christos Vlachos, Themos Stafylakis, Ion Androutsopoulos
2024 J jnl
CoRR
Lefteris Loukas, Nikolaos Smyrnioudis, Chrysa Dikonomaki, Spyros Barbakos, Anastasios Toumazatos, John Koutsikakis, Manolis Kyriakakis, Mary Georgiou, Stavros Vassos, John Pavlopoulos, Ion Androutsopoulos
2024 J jnl
CoRR
Odysseas S. Chlapanis, Dimitrios Galanis, Ion Androutsopoulos
2024 J jnl
Eur. J. Oper. Res.
Apostolos G. Katsafados, George N. Leledakis, Emmanouil G. Pyrgiotakis, Ion Androutsopoulos, Manos Fergadiotis
2024 conf
EACL (1)
Nefeli Gkouti, Prodromos Malakasiotis, Stavros Toumpis, Ion Androutsopoulos
2024 J jnl
CoRR
Nefeli Gkouti, Prodromos Malakasiotis, Stavros Toumpis, Ion Androutsopoulos
2024 conf
LREC/COLING
Anastasios Toumazatos, John Pavlopoulos, Ion Androutsopoulos, Stavros Vassos
2023 conf
CLEF (Working Notes)
Panagiotis Kaliosis, Georgios Moschovis, Foivos Charalampakos, John Pavlopoulos, Ion Androutsopoulos
2023 conf
EMNLP (Findings)
Ilias Stogiannidis, Stavros Vassos, Prodromos Malakasiotis, Ion Androutsopoulos
2023 J jnl
CoRR
Ilias Stogiannidis, Stavros Vassos, Prodromos Malakasiotis, Ion Androutsopoulos
2023 J jnl
Comput. Linguistics
Thea Sommerschield, Yannis M. Assael, John Pavlopoulos, Vanessa Stefanak, Andrew W. Senior, Chris Dyer, John Bodel, Jonathan Prag, Ion Androutsopoulos, Nando de Freitas
2022 conf
CLEF (Working Notes)
Foivos Charalampakos, Giorgos Zachariadis, John Pavlopoulos, Vasilis Karatzas, Christoforos Trakas, Ion Androutsopoulos
2022 conf
BioNLP@ACL
Dimitris Pappas, Prodromos Malakasiotis, Ion Androutsopoulos
2022 J jnl
CoRR
Dimitris Pappas, Prodromos Malakasiotis, Ion Androutsopoulos
2022 J jnl
Nat. Lang. Eng.
Katerina Papantoniou, Panagiotis Papadakos, Theodore Patkos, George Flouris, Ion Androutsopoulos, Dimitris Plexousakis
2022 J jnl
Knowl. Inf. Syst.
John Pavlopoulos, Vasiliki Kougia, Ion Androutsopoulos, Dimitris Papamichail
2022 conf
ACL (1)
Lefteris Loukas, Manos Fergadiotis, Ilias Chalkidis, Eirini Spyropoulou, Prodromos Malakasiotis, Ion Androutsopoulos, Georgios Paliouras
2022 J jnl
CoRR
Lefteris Loukas, Manos Fergadiotis, Ilias Chalkidis, Eirini Spyropoulou, Prodromos Malakasiotis, Ion Androutsopoulos, Georgios Paliouras
2022 conf
ACL (1)
John Pavlopoulos, Léo Laugier, Alexandros Xenos, Jeffrey Sorensen, Ion Androutsopoulos
2022 conf
ACL (1)
Ilias Chalkidis, Abhik Jana, Dirk Hartung, Michael J. Bommarito II, Ion Androutsopoulos, Daniel Martin Katz, Nikolaos Aletras
2022 conf
NLLP@EMNLP
Dimitris Mamakas, Petros Tsotsi, Ion Androutsopoulos, Ilias Chalkidis
2022 J jnl
CoRR
Dimitris Mamakas, Petros Tsotsi, Ion Androutsopoulos, Ilias Chalkidis
2022 conf
SETN
Stratos Xenouleas, Alexia Tsoukara, Giannis Panagiotakis, Ilias Chalkidis, Ion Androutsopoulos
2022 J jnl
CoRR
Stratos Xenouleas, Alexia Tsoukara, Giannis Panagiotakis, Ilias Chalkidis, Ion Androutsopoulos
2022 J jnl
Nat.
Yannis M. Assael, Thea Sommerschield, Brendan Shillingford, Mahyar Bordbar, John Pavlopoulos, Marita Chatzipanagiotou, Ion Androutsopoulos, Jonathan Prag, Nando de Freitas
2022 J jnl
First Monday
Alexandros Xenos, John Pavlopoulos, Ion Androutsopoulos, Lucas Dixon, Jeffrey Sorensen, Léo Laugier
2021 conf
ACL/IJCNLP (1)
Dimitris Pappas, Ion Androutsopoulos
2021 J jnl
CoRR
Dimitris Pappas, Ion Androutsopoulos
2021 conf
CLEF (Working Notes)
Foivos Charalampakos, Vasilis Karatzas, Vasiliki Kougia, John Pavlopoulos, Ion Androutsopoulos
2021 J jnl
CoRR
Katerina Papantoniou, Panagiotis Papadakos, Theodore Patkos, Giorgos Flouris, Ion Androutsopoulos, Dimitris Plexousakis
2021 J jnl
CoRR
John Pavlopoulos, Vasiliki Kougia, Ion Androutsopoulos, Dimitris Papamichail
2021 J jnl
CoRR
Lefteris Loukas, Manos Fergadiotis, Ion Androutsopoulos, Prodromos Malakasiotis
2021 J jnl
CoRR
Ilias Chalkidis, Abhik Jana, Dirk Hartung, Michael J. Bommarito II, Ion Androutsopoulos, Daniel Martin Katz, Nikolaos Aletras
2021 conf
EMNLP (1)
Ilias Chalkidis, Manos Fergadiotis, Ion Androutsopoulos
2021 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Ion Androutsopoulos
2021 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Ion Androutsopoulos
2021 conf
NAACL-HLT
Ilias Chalkidis, Manos Fergadiotis, Dimitrios Tsarapatsanis, Nikolaos Aletras, Ion Androutsopoulos, Prodromos Malakasiotis
2021 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Dimitrios Tsarapatsanis, Nikolaos Aletras, Ion Androutsopoulos, Prodromos Malakasiotis
2021 ed.
NLLP@EMNLP
Nikolaos Aletras, Ion Androutsopoulos, Leslie Barrett, Catalina Goanta, Daniel Preotiuc-Pietro
2021 conf
SemEval@ACL/IJCNLP
John Pavlopoulos, Jeffrey Sorensen, Léo Laugier, Ion Androutsopoulos
2021 J jnl
CoRR
Alexandros Xenos, John Pavlopoulos, Ion Androutsopoulos, Lucas Dixon, Jeffrey Sorensen, Léo Laugier
2020 conf
CLEF (Working Notes)
Basil Karatzas, John Pavlopoulos, Vasiliki Kougia, Ion Androutsopoulos
2020 conf
CLEF (Working Notes)
Dimitris Pappas, Petros Stavropoulos, Ion Androutsopoulos
2020 conf
EMNLP (1)
Ilias Chalkidis, Manos Fergadiotis, Sotiris Kotitsas, Prodromos Malakasiotis, Nikolaos Aletras, Ion Androutsopoulos
2020 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Sotiris Kotitsas, Prodromos Malakasiotis, Nikolaos Aletras, Ion Androutsopoulos
2020 J jnl
CoRR
Petros Stavropoulos, Dimitris Pappas, Ion Androutsopoulos, Ryan T. McDonald
2020 conf
BioNLP
Dimitris Pappas, Petros Stavropoulos, Ion Androutsopoulos, Ryan T. McDonald
2020 conf
EMNLP (Findings)
Giorgos Vernikos, Katerina Margatina, Alexandra Chronopoulou, Ion Androutsopoulos
2020 J jnl
CoRR
Giorgos Vernikos, Katerina Margatina, Alexandra Chronopoulou, Ion Androutsopoulos
2020 conf
SETN
John Koutsikakis, Ilias Chalkidis, Prodromos Malakasiotis, Ion Androutsopoulos
2020 J jnl
CoRR
John Koutsikakis, Ilias Chalkidis, Prodromos Malakasiotis, Ion Androutsopoulos
2020 conf
EMNLP (Findings)
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Nikolaos Aletras, Ion Androutsopoulos
2020 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Nikolaos Aletras, Ion Androutsopoulos
2020 conf
CLEF
Vasiliki Kougia, John Pavlopoulos, Ion Androutsopoulos
2020 ed.
NLLP@KDD
Nikolaos Aletras, Ion Androutsopoulos, Leslie Barrett, Adam Meyers, Daniel Preotiuc-Pietro
2020 ed.
SETN
Constantine D. Spyropoulos, Iraklis Varlamis, Ion Androutsopoulos, Prodromos Malakasiotis
2020 A* conf
ACL
John Pavlopoulos, Jeffrey Sorensen, Lucas Dixon, Nithum Thain, Ion Androutsopoulos
2020 J jnl
CoRR
John Pavlopoulos, Jeffrey Sorensen, Lucas Dixon, Nithum Thain, Ion Androutsopoulos
2019 J jnl
CoRR
Vasiliki Kougia, John Pavlopoulos, Ion Androutsopoulos
2019 conf
CLEF (Working Notes)
Vasiliki Kougia, John Pavlopoulos, Ion Androutsopoulos
2019 conf
PKDD/ECML Workshops (2)
Dimitris Pappas, Ryan T. McDonald, Georgios-Ioannis Brokos, Ion Androutsopoulos
2019 conf
SemEval@NAACL-HLT
John Pavlopoulos, Nithum Thain, Lucas Dixon, Ion Androutsopoulos
2019 conf
BioNLP@ACL
Sotiris Kotitsas, Dimitris Pappas, Ion Androutsopoulos, Ryan T. McDonald, Marianna Apidianaki
2019 J jnl
CoRR
Sotiris Kotitsas, Dimitris Pappas, Ion Androutsopoulos, Ryan T. McDonald, Marianna Apidianaki
2019 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Nikolaos Aletras, Ion Androutsopoulos
2019 conf
ACL (1)
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Ion Androutsopoulos
2019 J jnl
CoRR
Ilias Chalkidis, Manos Fergadiotis, Prodromos Malakasiotis, Ion Androutsopoulos
2019 conf
ACL (1)
Ilias Chalkidis, Ion Androutsopoulos, Nikolaos Aletras
2019 J jnl
CoRR
Ilias Chalkidis, Ion Androutsopoulos, Nikolaos Aletras
2019 J jnl
CoRR
Christos Baziotis, Ion Androutsopoulos, Ioannis Konstas, Alexandros Potamianos
2019 conf
NAACL-HLT (1)
Christos Baziotis, Ion Androutsopoulos, Ioannis Konstas, Alexandros Potamianos
2019 conf
EMNLP/IJCNLP (1)
Stratos Xenouleas, Prodromos Malakasiotis, Marianna Apidianaki, Ion Androutsopoulos
2019 J jnl
CoRR
Stratos Xenouleas, Prodromos Malakasiotis, Marianna Apidianaki, Ion Androutsopoulos
2019 conf
BioNLP@ACL
Manolis Kyriakakis, Ion Androutsopoulos, Artur Saudabayev, Joan Ginés i Ametllé
2019 J jnl
CoRR
Manolis Kyriakakis, Ion Androutsopoulos, Joan Ginés i Ametllé, Artur Saudabayev
2018 J jnl
CoRR
Georgios-Ioannis Brokos, Polyvios Liosis, Ryan T. McDonald, Dimitris Pappas, Ion Androutsopoulos
2018 B conf
LREC
Dimitris Pappas, Ion Androutsopoulos, Haris Papageorgiou
2018 J jnl
CoRR
Ryan T. McDonald, Georgios-Ioannis Brokos, Ion Androutsopoulos
2018 A* conf
EMNLP
Ryan T. McDonald, George Brokos, Ion Androutsopoulos
2018 J jnl
CoRR
Gerasimos Lampouras, Ion Androutsopoulos
2018 J jnl
CoRR
Gerasimos Lampouras, Ion Androutsopoulos
2018 conf
SETN
Michail Vougioukas, Ion Androutsopoulos, Georgios Paliouras
2018 conf
ACL (2)
Ilias Chalkidis, Ion Androutsopoulos, Achilleas Michos
2018 J jnl
CoRR
Ilias Chalkidis, Ion Androutsopoulos, Achilleas Michos
2018 conf
ISWC (1)
Vayianos Pertsas, Panos Constantopoulos, Ion Androutsopoulos
2017 C conf
JURIX
Ilias Chalkidis, Ion Androutsopoulos
2017 A conf
UMAP
Michail Vougioukas, Ion Androutsopoulos, Georgios Paliouras
2017 conf
ALW@ACL
John Pavlopoulos, Prodromos Malakasiotis, Ion Androutsopoulos
2017 J jnl
CoRR
John Pavlopoulos, Prodromos Malakasiotis, Ion Androutsopoulos
2017 A* conf
EMNLP
John Pavlopoulos, Prodromos Malakasiotis, Ion Androutsopoulos
2017 C conf
ICAIL
Ilias Chalkidis, Ion Androutsopoulos, Achilleas Michos
2017 J jnl
CoRR
Michail Vougioukas, Ion Androutsopoulos, Georgios Paliouras
2017 conf
NLPmJ@EMNLP
John Pavlopoulos, Prodromos Malakasiotis, Juli Bakagianni, Ion Androutsopoulos
2017 J jnl
CoRR
John Pavlopoulos, Prodromos Malakasiotis, Juli Bakagianni, Ion Androutsopoulos
2016 conf
SemEval@NAACL-HLT
Dionysios Xenos, Panagiotis Theodorakakos, John Pavlopoulos, Prodromos Malakasiotis, Ion Androutsopoulos
2016 conf
SemEval@NAACL-HLT
Maria Pontiki, Dimitris Galanis, Haris Papageorgiou, Ion Androutsopoulos, Suresh Manandhar, Mohammad Al-Smadi, Mahmoud Al-Ayyoub, Yanyan Zhao, Bing Qin, Orphée De Clercq, Véronique Hoste, Marianna Apidianaki, Xavier Tannier, Natalia V. Loukachevitch, Evgeniy V. Kotelnikov, Núria Bel, Salud María Jiménez-Zafra, Gülsen Eryigit
2016 conf
BioNLP@ACL
Georgios-Ioannis Brokos, Prodromos Malakasiotis, Ion Androutsopoulos
2016 J jnl
CoRR
Georgios-Ioannis Brokos, Prodromos Malakasiotis, Ion Androutsopoulos
2016 conf
SemEval@NAACL-HLT
Stavros Giorgis, Apostolos Rousas, John Pavlopoulos, Prodromos Malakasiotis, Ion Androutsopoulos
2015 J jnl
BMC Bioinform.
George Tsatsaronis, Georgios Balikas, Prodromos Malakasiotis, Ioannis Partalas, Matthias Zschunke, Michael R. Alvers, Dirk Weissenborn, Anastasia Krithara, Sergios Petridis, Dimitris Polychronopoulos, Yannis Almirantis, John Pavlopoulos, Nicolas Baskiotis, Patrick Gallinari, Thierry Artières, Axel-Cyrille Ngonga Ngomo, Norman Heino, Éric Gaussier, Liliana Barrio-Alvers, Michael Schroeder, Ion Androutsopoulos, Georgios Paliouras
2015 conf
CogInfoCom
Dimitris Pappas, Ion Androutsopoulos, Haris Papageorgiou
2015 conf
CLEF (Working Notes)
Prodromos Malakasiotis, Emmanouil Archontakis, Ion Androutsopoulos, Dimitrios Galanis, Harris Papageorgiou
2015 J jnl
Data Min. Knowl. Discov.
Aris Kosmopoulos, Ioannis Partalas, Éric Gaussier, Georgios Paliouras, Ion Androutsopoulos
2015 J jnl
CoRR
Ioannis Partalas, Aris Kosmopoulos, Nicolas Baskiotis, Thierry Artières, George Paliouras, Éric Gaussier, Ion Androutsopoulos, Massih-Reza Amini, Patrick Gallinari
2015 J jnl
CoRR
Aris Kosmopoulos, Georgios Paliouras, Ion Androutsopoulos
2015 conf
SemEval@NAACL-HLT
Maria Pontiki, Dimitris Galanis, Haris Papageorgiou, Suresh Manandhar, Ion Androutsopoulos
2014 J jnl
CoRR
Ion Androutsopoulos, Gerasimos Lampouras, Dimitrios Galanis
2014 A conf
EACL
John Pavlopoulos, Ion Androutsopoulos
2014 conf
SemEval@COLING
Maria Pontiki, Dimitris Galanis, John Pavlopoulos, Harris Papageorgiou, Ion Androutsopoulos, Suresh Manandhar
2014 conf
CLEF
Aris Kosmopoulos, Georgios Paliouras, Ion Androutsopoulos
2014 A conf
WSDM
Ioannis Partalas, Massih-Reza Amini, Ion Androutsopoulos, Thierry Artières, Patrick Gallinari, Éric Gaussier, Georgios Paliouras
2013 J jnl
CoRR
Aris Kosmopoulos, Ioannis Partalas, Éric Gaussier, Georgios Paliouras, Ion Androutsopoulos
2013 J jnl
J. Artif. Intell. Res.
Ion Androutsopoulos, Gerasimos Lampouras, Dimitrios Galanis
2013 conf
ENLG
Gerasimos Lampouras, Ion Androutsopoulos
2013 conf
ACL (2)
Gerasimos Lampouras, Ion Androutsopoulos
2012 conf
AAAI Fall Symposium: Information Retrieval and Knowledge Discovery in Biomedical Text
George Tsatsaronis, Michael Schroeder, Georgios Paliouras, Yannis Almirantis, Ion Androutsopoulos, Éric Gaussier, Patrick Gallinari, Thierry Artières, Michael R. Alvers, Matthias Zschunke, Axel-Cyrille Ngonga Ngomo
2012 B conf
COLING
Dimitrios Galanis, Gerasimos Lampouras, Ion Androutsopoulos
2012 conf
SETN
Vicky Panagiotopoulou, Iraklis Varlamis, Ion Androutsopoulos, George Tsatsaronis
2011 A* conf
EMNLP
Prodromos Malakasiotis, Ion Androutsopoulos
2010 J jnl
J. Artif. Intell. Res.
Ion Androutsopoulos, Prodromos Malakasiotis
2010 conf
HLT-NAACL
Dimitrios Galanis, Ion Androutsopoulos
2009 J jnl
CoRR
Ion Androutsopoulos, Prodromos Malakasiotis
2009 A conf
EACL (Demos)
Stasinos Konstantopoulos, Athanasios Tegos, Dimitris Bilidas, Ion Androutsopoulos, Gerasimos Lampouras, Colin Matheson, Olivier Deroo, Prodromos Malakasiotis
2009 A conf
EACL (Demos)
Dimitrios Galanis, George Karakatsiotis, Gerasimos Lampouras, Ion Androutsopoulos
2009 J jnl
Int. J. Digit. Curation
Panos Constantopoulos, Costis J. Dallas, Ion Androutsopoulos, Stavros Angelis, Antonios Deligiannakis, Dimitris Gavrilis, Yannis Kotidis, Christos Papatheodorou
2009 A* conf
EMNLP
Gerasimos Lampouras, Ion Androutsopoulos
2008 J jnl
INFORMS Trans. Educ.
Evangelos F. Magirou, Dimitrios K. Vassilakis, Ion Androutsopoulos
2007 conf
CEAS
Dimitrios K. Vassilakis, Ion Androutsopoulos, Evangelos F. Magirou
2007 conf
ENLG
Dimitrios Galanis, Ion Androutsopoulos
2007 conf
ACL-PASCAL@ACL
Prodromos Malakasiotis, Ion Androutsopoulos
2007 J jnl
Int. J. Artif. Intell. Tools
Georgios Lucarelli, Xenofon Vasilakos, Ion Androutsopoulos
2007 J jnl
Nat. Lang. Eng.
Ion Androutsopoulos, Jon Oberlander, Vangelis Karkaletsis
2007 A* conf
IJCAI
George Tsatsaronis, Michalis Vazirgiannis, Ion Androutsopoulos
2006 conf
SETN
Georgios Lucarelli, Ion Androutsopoulos
2006 conf
CEAS
Vangelis Metsis, Ion Androutsopoulos, Georgios Paliouras
2005 conf
CEAS
Ion Androutsopoulos, Evangelos F. Magirou, Dimitrios K. Vassilakis
2005 conf
HLT/EMNLP
Ion Androutsopoulos, Dimitrios Galanis
2005 conf
ENLG
Ion Androutsopoulos, Spyros Kallonis, Vangelis Karkaletsis
2004 conf
CEAS
Eirinaios Michelakis, Ion Androutsopoulos, Georgios Paliouras, Georgios Sakkis, Panagiotis Stamatopoulos
2004 B conf
COLING
Spyridoula Miliaraki, Ion Androutsopoulos
2003 J jnl
Inf. Retr.
Georgios Sakkis, Ion Androutsopoulos, Georgios Paliouras, Vangelis Karkaletsis, Constantine D. Spyropoulos, Panagiotis Stamatopoulos
2003 J jnl
CoRR
Aggeliki Dimitromanolaki, Ion Androutsopoulos
2003 conf
ENLG@EACL
Aggeliki Dimitromanolaki, Ion Androutsopoulos
2003 J jnl
IEEE Intell. Syst.
Amy Isard, Jon Oberlander, Colin Matheson, Ion Androutsopoulos
2002 J jnl
CoRR
Georgios Petasis, Vangelis Karkaletsis, Georgios Paliouras, Ion Androutsopoulos, Constantine D. Spyropoulos
2002 B conf
LREC
Georgios Petasis, Vangelis Karkaletsis, Georgios Paliouras, Ion Androutsopoulos, Constantine D. Spyropoulos
2002 conf
SETN
Ion Androutsopoulos, Dimitris Spiliotopoulos, Konstantinos Stamatakis, Aggeliki Dimitromanolaki, Vangelis Karkaletsis, Constantine D. Spyropoulos
2001 conf
Panhellenic Conference on Informatics
Georgios Petasis, Vangelis Karkaletsis, Dimitra Farmakiotou, Ion Androutsopoulos, Constantine D. Spyropoulos
2001 J jnl
CoRR
Ion Androutsopoulos, Vassiliki Kokkinaki, Aggeliki Dimitromanolaki, Jonathan Calder, Jon Oberlander, Elena Not
2001 A* conf
EMNLP
Georgios Sakkis, Ion Androutsopoulos, Georgios Paliouras, Vangelis Karkaletsis, Constantine D. Spyropoulos, Panagiotis Stamatopoulos
2001 J jnl
CoRR
Georgios Sakkis, Ion Androutsopoulos, Georgios Paliouras, Vangelis Karkaletsis, Constantine D. Spyropoulos, Panagiotis Stamatopoulos
2000 J jnl
CoRR
Ion Androutsopoulos, John Koutsias, Konstantinos Chandrinos, Constantine D. Spyropoulos
2000 J jnl
CoRR
Ion Androutsopoulos, John Koutsias, Konstantinos Chandrinos, Georgios Paliouras, Constantine D. Spyropoulos
2000 A* conf
SIGIR
Ion Androutsopoulos, John Koutsias, Konstantinos Chandrinos, Constantine D. Spyropoulos
2000 conf
ECDL
Konstantinos Chandrinos, Ion Androutsopoulos, Georgios Paliouras, Constantine D. Spyropoulos
2000 conf
Natural Language Processing
Georgios Paliouras, Vangelis Karkaletsis, Ion Androutsopoulos, Constantine D. Spyropoulos
2000 J jnl
CoRR
Ion Androutsopoulos, Georgios Paliouras, Vangelis Karkaletsis, Georgios Sakkis, Constantine D. Spyropoulos, Panagiotis Stamatopoulos
2000 B conf
COLING
Ion Androutsopoulos, Robert Dale
2000 J jnl
CoRR
Ion Androutsopoulos, Robert Dale
1999 J jnl
CoRR
Georgios Petasis, Georgios Paliouras, Vangelis Karkaletsis, Constantine D. Spyropoulos, Ion Androutsopoulos
1999 J jnl
CoRR
Ion Androutsopoulos
1998 J jnl
CoRR
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
1998 J jnl
Nat. Lang. Eng.
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
1996 J jnl
CoRR
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
1996 J jnl
CoRR
Ion Androutsopoulos
1996
Ion Androutsopoulos
1995 conf
Temporal Databases
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
1995 J jnl
CoRR
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
1995 J jnl
Nat. Lang. Eng.
Ion Androutsopoulos, Graeme D. Ritchie, Peter Thanisch
redb/queries.py
← Index redb/queries.py python
"""
Database query functions for REDB.

Contains all functions that query ClickHouse for sample metadata,
deduplication checks, and catalog lookups.
"""
import os
import ast
import clickhouse_connect
from typing import List, Optional, Dict

from redb import settings
from redb.s3_utils import generate_s3_key_from_hash


def get_supported_formats(magika_filter: Optional[str] = None) -> List[str]:
    """
    Get supported file formats from SUPPORTED_FORMATS env variable or magika_filter override.
    Expected format: SUPPORTED_FORMATS=['pebin', 'elf']

    Args:
        magika_filter: Optional single format to filter by (overrides env var)

    Returns:
        List of supported format strings, defaults to ['pebin'] if not set
    """
    # If magika_filter is provided, use it as the only format
    if magika_filter:
        return [magika_filter]

    formats_str = os.getenv('SUPPORTED_FORMATS', "['pebin']")
    try:
        formats = ast.literal_eval(formats_str)
        if isinstance(formats, list) and all(isinstance(f, str) for f in formats):
            return formats
        else:
            print(f"[WARNING] SUPPORTED_FORMATS must be a list of strings, got: {formats_str}")
            return ['pebin']
    except (ValueError, SyntaxError) as e:
        print(f"[WARNING] Failed to parse SUPPORTED_FORMATS '{formats_str}': {e}")
        return ['pebin']


def get_db_catalog_connection():
    """Create and return a ClickHouse client for catalog queries"""
    return clickhouse_connect.get_client(
        host=os.getenv("DB_HOST"),
        port=int(os.getenv("DB_PORT", "8123")),
        verify=os.getenv("DB_ENFORCE_SSL", "False").lower() == "true",
        username=os.getenv("DB_USER"),
        password=os.getenv("DB_PASSWORD"),
        database=os.getenv("DB_NAME")
    )


def fetch_s3_objects_by_repository(repository: Optional[str], index_prefix: str, decompile: bool, notes: Optional[str] = None, magika_filter: Optional[str] = None, yara_scan: bool = False, force: bool = False) -> List[Dict]:
    """
    Query the ClickHouse repository_upload_sessions table to get samples.
    S3 bucket comes from env var, S3 key is derived from sha256.

    Args:
        repository: Repository name (e.g., "bazaar", "malshare"). If None or "all-repos", queries all repositories.
        index_prefix: Table prefix for checking existing samples
        decompile: Whether we're in decompile mode (affects which table to check for existing)
        notes: Optional filter for notes field
        magika_filter: Optional single filetype to filter by (overrides SUPPORTED_FORMATS)
        yara_scan: Whether we're in YARA-only mode (skips "already processed" check)

    Returns:
        List of dicts with sha256, s3_bucket, s3_key for each sample
    """
    try:
        client = get_db_catalog_connection()
        s3_bucket = os.getenv('S3_BUCKET')

        if not s3_bucket:
            print("[ERROR] S3_BUCKET environment variable is required")
            return []

        # Get supported formats from env or magika_filter override
        supported_formats = get_supported_formats(magika_filter)
        print(f"[INFO] Querying for supported formats: {supported_formats}")

        # Build query - repository filter is optional
        # Join with catalog_samples to get first_seen date
        if repository and repository != "all-repos":
            query = """
                SELECT DISTINCT rus.sha256, rus.filetype_magika, cs.first_seen
                FROM repository_upload_sessions rus
                LEFT JOIN catalog_samples cs ON rus.sha256 = cs.sha256
                WHERE rus.repository = %(repo)s
                  AND rus.filetype_magika IN %(formats)s
            """
            params = {"repo": repository, "formats": supported_formats}
        else:
            # Query all repositories
            query = """
                SELECT DISTINCT rus.sha256, rus.filetype_magika, cs.first_seen
                FROM repository_upload_sessions rus
                LEFT JOIN catalog_samples cs ON rus.sha256 = cs.sha256
                WHERE rus.filetype_magika IN %(formats)s
            """
            params = {"formats": supported_formats}

        if notes:
            query += " AND rus.notes LIKE %(notes)s"
            params["notes"] = f"%{notes}%"

        # Execute query and convert to list of dictionaries
        result = client.query(query, parameters=params)

        # Build rows with s3_bucket and s3_key derived from sha256
        rows = []
        for row in result.result_rows:
            sha256 = row[0]
            # Handle binary string if needed
            if isinstance(sha256, bytes):
                sha256 = sha256.decode('utf-8')

            rows.append({
                'sha256': sha256,
                's3_bucket': s3_bucket,
                's3_key': generate_s3_key_from_hash(sha256),
                'filetype_magika': row[1],
                'first_seen': row[2]  # From catalog_samples (None if not found)
            })

        print(f"[DEBUG] Found {len(rows)} objects in repository_upload_sessions for repository {repository}")

        # Extract SHA256 hashes from the results for bulk checking
        sha256_list = [row.get('sha256') for row in rows if row.get('sha256')]

        if sha256_list and not force:
            # Check which hashes are already in the database
            existing_hashes = is_in_db_bulk(sha256_list, index_prefix, decompile, yara_scan, magika_filter)
            print(f"[INFO] Found {len(existing_hashes)} objects already in database")

            # Filter out rows with existing hashes
            rows = [row for row in rows if row.get('sha256') not in existing_hashes]
            print(f"[INFO] After filtering, {len(rows)} objects remain to be processed")
        elif force:
            print(f"[INFO] Force mode: skipping deduplication check, processing all {len(sha256_list)} objects")

        # Randomize the order of rows before returning
        import random
        random.shuffle(rows)

        return rows
    except Exception as e:
        print(f"[ERROR] Failed to query repository_upload_sessions: {e}")
        return []
    finally:
        if 'client' in locals():
            client.close()


def fetch_s3_objects_by_date_range(
    index_prefix: str,
    decompile: bool,
    start_date: str,
    end_date: str,
    repository: Optional[str] = None,
    notes: Optional[str] = None,
    magika_filter: Optional[str] = None,
    yara_scan: bool = False,
    force: bool = False,
    analyzed: bool = False
) -> List[Dict]:
    """
    Query samples from catalog_samples by first_seen date range, filtered to only include
    samples that exist in repository_upload_sessions (bulk/repo uploads only).

    Args:
        index_prefix: Table prefix for checking existing samples
        decompile: Whether we're in decompile mode
        start_date: Start date (inclusive) in YYYY-MM-DD format
        end_date: End date (exclusive) in YYYY-MM-DD format
        repository: Optional repository filter
        notes: Optional notes filter
        magika_filter: Optional single filetype to filter by (overrides SUPPORTED_FORMATS)
        yara_scan: Whether we're in YARA-only mode (skips "already processed" check)
        analyzed: Filter to only samples already in basic_properties (cross-database join)

    Returns:
        List of dicts with sha256, s3_bucket, s3_key for each sample
    """
    try:
        client = get_db_catalog_connection()
        s3_bucket = os.getenv('S3_BUCKET')

        if not s3_bucket:
            print("[ERROR] S3_BUCKET environment variable is required")
            return []

        # Get supported formats from env or magika_filter override
        supported_formats = get_supported_formats(magika_filter)
        print(f"[INFO] Querying for supported formats: {supported_formats}")

        # Join catalog_samples with repository_upload_sessions to:
        # 1. Filter by first_seen date from catalog_samples
        # 2. Only include samples that exist in repository_upload_sessions (not user uploads)
        # 3. Optionally filter to only already-analyzed samples (in basic_properties)
        analyzed_join = ""
        if analyzed:
            basic_table = f"{index_prefix}_basic_properties"
            analyzed_join = f"INNER JOIN {basic_table} bp ON cs.sha256 = bp.sha256"
            print(f"[INFO] Filtering to already-analyzed samples in {basic_table}")

        query = f"""
            SELECT DISTINCT cs.sha256, rus.filetype_magika, cs.first_seen
            FROM catalog_samples cs
            INNER JOIN repository_upload_sessions rus ON cs.sha256 = rus.sha256
            {analyzed_join}
            WHERE cs.first_seen >= %(start_date)s
              AND cs.first_seen < %(end_date)s
              AND rus.filetype_magika IN %(formats)s
        """
        params = {
            "start_date": start_date,
            "end_date": end_date,
            "formats": supported_formats
        }

        if repository:
            query += " AND rus.repository = %(repo)s"
            params["repo"] = repository

        if notes:
            query += " AND rus.notes LIKE %(notes)s"
            params["notes"] = f"%{notes}%"

        # Execute query
        result = client.query(query, parameters=params)

        # Build rows with s3_bucket and s3_key derived from sha256
        rows = []
        for row in result.result_rows:
            sha256 = row[0]
            # Handle binary string if needed
            if isinstance(sha256, bytes):
                sha256 = sha256.decode('utf-8')

            rows.append({
                'sha256': sha256,
                's3_bucket': s3_bucket,
                's3_key': generate_s3_key_from_hash(sha256),
                'filetype_magika': row[1],
                'first_seen': row[2]  # From catalog_samples
            })

        date_info = f"from {start_date} to {end_date}"
        repo_info = f" for repository {repository}" if repository else ""
        print(f"[DEBUG] Found {len(rows)} objects in catalog_samples {date_info}{repo_info}")

        # Extract SHA256 hashes from the results for bulk checking
        sha256_list = [row.get('sha256') for row in rows if row.get('sha256')]

        if sha256_list and not force:
            # Check which hashes are already in the database
            existing_hashes = is_in_db_bulk(sha256_list, index_prefix, decompile, yara_scan, magika_filter)
            print(f"[INFO] Found {len(existing_hashes)} objects already in database")

            # Filter out rows with existing hashes
            rows = [row for row in rows if row.get('sha256') not in existing_hashes]
            print(f"[INFO] After filtering, {len(rows)} objects remain to be processed")
        elif force:
            print(f"[INFO] Force mode: skipping deduplication check, processing all {len(sha256_list)} objects")

        # Randomize the order of rows before returning
        import random
        random.shuffle(rows)

        return rows
    except Exception as e:
        print(f"[ERROR] Failed to query catalog_samples by date range: {e}")
        return []
    finally:
        if 'client' in locals():
            client.close()


def fetch_analyzed_samples(
    index_prefix: str,
    decompile: bool,
    magika_filter: Optional[str] = None,
    yara_scan: bool = False,
    force: bool = False,
    rerun: bool = False
) -> List[Dict]:
    """
    Query samples from basic_properties that have already been analyzed.
    Useful for reprocessing with decompilation or specific modules.

    Args:
        index_prefix: Table prefix for ClickHouse
        decompile: Whether we're in decompile mode (affects filtering)
        magika_filter: Optional single filetype to filter by (overrides SUPPORTED_FORMATS)
        yara_scan: Whether we're in YARA-only mode (skips decompile filtering)
        force: Skip all deduplication checks when True
        rerun: Query disassembled table directly (only already-disassembled samples)

    Returns:
        List of dicts with sha256, s3_bucket, s3_key for each analyzed sample
    """
    try:
        client = settings.create_clickhouse_client()
        s3_bucket = os.getenv('S3_BUCKET')

        if not s3_bucket:
            print("[ERROR] S3_BUCKET environment variable is required")
            return []

        # Rerun mode: query directly from disassembled_functions_references
        # instead of basic_properties. This targets only samples that already
        # went through binja successfully.
        if rerun:
            disassembled_table = _get_code_dedup_table(magika_filter)
            basic_table = f"{index_prefix}_basic_properties"

            # Join with basic_properties to get filetype_magika and apply format filters
            conditions = []
            params = {}

            if magika_filter:
                conditions.append("bp.filetype_magika = %(magika)s")
                params["magika"] = magika_filter
            else:
                supported_formats = get_supported_formats()
                conditions.append("bp.filetype_magika IN %(formats)s")
                params["formats"] = supported_formats

            query = (
                f"SELECT DISTINCT d.sha256, bp.filetype_magika "
                f"FROM {disassembled_table} d FINAL "
                f"INNER JOIN {basic_table} bp FINAL ON d.sha256 = bp.sha256"
            )
            if conditions:
                query += " WHERE " + " AND ".join(conditions)

            result = client.query(query, parameters=params)

            rows = []
            for row in result.result_rows:
                sha256 = row[0]
                if isinstance(sha256, bytes):
                    sha256 = sha256.decode('utf-8')
                rows.append({
                    'sha256': sha256,
                    's3_bucket': s3_bucket,
                    's3_key': generate_s3_key_from_hash(sha256),
                    'filetype_magika': row[1]
                })

            print(f"[INFO] Rerun mode: found {len(rows)} already-disassembled samples in {disassembled_table}")

            import random
            random.shuffle(rows)
            return rows

        basic_table = f"{index_prefix}_basic_properties"

        # Build query
        conditions = []
        params = {}

        if magika_filter:
            conditions.append("filetype_magika = %(magika)s")
            params["magika"] = magika_filter
        else:
            supported_formats = get_supported_formats()
            conditions.append("filetype_magika IN %(formats)s")
            params["formats"] = supported_formats

        query = f"SELECT DISTINCT sha256, filetype_magika FROM {basic_table} FINAL"
        if conditions:
            query += " WHERE " + " AND ".join(conditions)

        result = client.query(query, parameters=params)

        rows = []
        for row in result.result_rows:
            sha256 = row[0]
            if isinstance(sha256, bytes):
                sha256 = sha256.decode('utf-8')
            rows.append({
                'sha256': sha256,
                's3_bucket': s3_bucket,
                's3_key': generate_s3_key_from_hash(sha256),
                'filetype_magika': row[1]
            })

        print(f"[DEBUG] Found {len(rows)} analyzed samples in {basic_table}")

        # In YARA-only mode, filter out already-scanned samples (unless force)
        if yara_scan and not force:
            sha256_list = [r['sha256'] for r in rows]
            if sha256_list:
                already_scanned = _check_yara_matches_bulk(sha256_list)
                print(f"[INFO] Found {len(already_scanned)} already YARA-scanned samples")
                rows = [r for r in rows if r['sha256'].lower() not in already_scanned]
                print(f"[INFO] After filtering, {len(rows)} samples remain for YARA scanning")
        # In decompile mode, filter out already-disassembled samples (unless force)
        # We check disassembly (not decompilation) because disassembly is the ground truth:
        # disassembly always succeeds, decompilation may not, so a missing decompile
        # entry doesn't mean the sample wasn't analyzed.
        elif decompile and not force:
            sha256_list = [r['sha256'] for r in rows]
            if sha256_list:
                disassembled_table = _get_code_dedup_table(magika_filter)
                batch_size = 3900

                already_disassembled = set()
                for i in range(0, len(sha256_list), batch_size):
                    batch = sha256_list[i:i+batch_size]
                    placeholders = "','".join(batch)
                    check_query = f"SELECT DISTINCT sha256 FROM {disassembled_table} FINAL WHERE sha256 IN ('{placeholders}')"
                    check_result = client.query(check_query)
                    for check_row in check_result.result_rows:
                        hash_value = check_row[0]
                        if isinstance(hash_value, bytes):
                            hash_value = hash_value.decode('utf-8')
                        already_disassembled.add(hash_value)

                print(f"[INFO] Found {len(already_disassembled)} already-disassembled samples")
                rows = [r for r in rows if r['sha256'] not in already_disassembled]
                print(f"[INFO] After filtering, {len(rows)} samples remain for decompilation")
        elif force:
            print(f"[INFO] Force mode: returning all {len(rows)} analyzed samples")

        # Randomize the order of rows before returning
        import random
        random.shuffle(rows)

        return rows
    except Exception as e:
        print(f"[ERROR] Failed to query analyzed samples: {e}")
        return []
    finally:
        if 'client' in locals():
            client.close()


def is_in_db(sha256, index_prefix, client=None):
    """
    Check if a sample with given SHA256 already exists in the database

    Args:
        sha256: File's SHA256 hash
        index_prefix: Table prefix for ClickHouse
        client: Optional ClickHouse client instance

    Returns:
        bool: True if file exists, False otherwise
    """
    table = f"{index_prefix}_basic_properties"
    close_client = False

    try:
        if client is None:
            client = settings.create_clickhouse_client()
            close_client = True

        # No need for FINAL when checking existence with LIMIT 1
        # Any version of the row proves the sample exists
        query = f"SELECT 1 FROM {table} WHERE sha256 = %(sha256)s LIMIT 1"
        result = client.query(query, parameters={"sha256": sha256})

        return len(result.result_rows) > 0
    except Exception as e:
        print(f"[ERROR] Failed to check if file is in DB: {e}")
        return False
    finally:
        if close_client and client:
            client.close()


def _get_code_dedup_table(magika_filter: Optional[str] = None) -> str:
    """
    Return the code-analysis table used for deduplication based on filetype.

    APK samples are disassembled into code_apk_smali_methods_references;
    everything else uses {CLICKHOUSE_CODE_PREFIX}_disassembled_functions_references.
    """
    if magika_filter == "apk":
        return "code_apk_smali_methods_references"
    code_prefix = os.getenv("CLICKHOUSE_CODE_PREFIX", "code_binja")
    return f"{code_prefix}_disassembled_functions_references"


def is_in_code_db(sha256, client=None, filetype=None):
    """
    Check if a sample with given SHA256 has already been disassembled.

    Args:
        sha256: File's SHA256 hash
        client: Optional ClickHouse client instance
        filetype: Magika filetype label (e.g. 'apk') to pick the right code table

    Returns:
        bool: True if file has been disassembled, False otherwise
    """
    table = _get_code_dedup_table(filetype)
    close_client = False

    try:
        if client is None:
            client = settings.create_clickhouse_client()
            close_client = True

        query = f"SELECT 1 FROM {table} WHERE sha256 = %(sha256)s LIMIT 1"
        result = client.query(query, parameters={"sha256": sha256})

        return len(result.result_rows) > 0
    except Exception as e:
        print(f"[ERROR] Failed to check if file is in code DB: {e}")
        return False
    finally:
        if close_client and client:
            client.close()


def _check_yara_matches_bulk(sha256_list):
    """
    Check which samples have already been YARA-scanned by querying yara_matches.

    The yara_matches table uses FixedString(32) binary sha256, so we convert
    hex strings with unhex().

    Args:
        sha256_list: List of hex SHA256 strings to check

    Returns:
        set: Set of hex SHA256 hashes that already have YARA matches
    """
    # unhex('64hexchars') adds 9 chars overhead per entry vs plain '64hexchars'.
    # 3900 works for plain strings (~67 chars each = 261K) but overflows
    # max_query_size (262144) with unhex() wrapping (~73 chars each = 285K).
    # 3500 × 73 = 255K stays safely under the limit.
    batch_size = 3500
    already_scanned = set()

    try:
        client = settings.create_clickhouse_client()

        total_batches = (len(sha256_list) + batch_size - 1) // batch_size
        for batch_num, i in enumerate(range(0, len(sha256_list), batch_size), 1):
            batch = sha256_list[i:i+batch_size]
            unhex_list = ",".join(f"unhex('{h}')" for h in batch)
            query = f"SELECT DISTINCT hex(sha256) FROM yara_matches FINAL WHERE sha256 IN ({unhex_list})"
            result = client.query(query)
            for row in result.result_rows:
                hash_value = row[0]
                if isinstance(hash_value, bytes):
                    hash_value = hash_value.decode('utf-8')
                already_scanned.add(hash_value.lower())

            if batch_num % 100 == 0 or batch_num == total_batches:
                print(f"[INFO] YARA dedup progress: batch {batch_num}/{total_batches}, found {len(already_scanned)} so far")

        print(f"[INFO] YARA dedup: found {len(already_scanned)} already-scanned samples")
        return already_scanned

    except Exception as e:
        raise RuntimeError(f"YARA dedup query failed — aborting to prevent reprocessing all samples: {e}")
    finally:
        if 'client' in locals():
            client.close()


def is_in_db_bulk(sha256_list, index_prefix, decompile, yara_scan=False, magika_filter=None):
    """
    Check which samples from a list of SHA256 hashes should be skipped.

    Args:
        sha256_list: List of SHA256 hashes to check
        index_prefix: Table prefix for ClickHouse
        decompile: Whether we're in decompile mode
        yara_scan: Whether we're in YARA-only mode (checks yara_matches table)
        magika_filter: Magika filetype label (e.g. 'apk') to pick the right code table

    Returns:
        set: Set of SHA256 hashes that should be skipped
    """
    if not sha256_list:
        return set()

    # YARA-only mode: check yara_matches table for already-scanned samples
    if yara_scan:
        return _check_yara_matches_bulk(sha256_list)

    basic_table = f"{index_prefix}_basic_properties"
    batch_size = 3900

    try:
        client = settings.create_clickhouse_client()

        # Step 1: Check basic_properties (required for both modes)
        in_basic_properties = set()
        for i in range(0, len(sha256_list), batch_size):
            batch = sha256_list[i:i+batch_size]
            placeholders = "','".join(batch)
            query = f"SELECT DISTINCT sha256 FROM {basic_table} FINAL WHERE sha256 IN ('{placeholders}')"
            result = client.query(query)
            for row in result.result_rows:
                hash_value = row[0]
                if isinstance(hash_value, bytes):
                    hash_value = hash_value.decode('utf-8')
                in_basic_properties.add(hash_value)

        if not decompile:
            # Analysis mode: skip samples already in basic_properties
            return in_basic_properties

        # Decompile mode: skip samples NOT in basic_properties + already decompiled
        not_in_basic = set(sha256_list) - in_basic_properties

        if not in_basic_properties:
            return set(sha256_list)  # None ready for decompilation

        # Step 2: Check disassembled table for samples that ARE in basic_properties
        # Disassembly is the ground truth for code analysis (it always succeeds,
        # unlike decompilation which may fail).
        disassembled_table = _get_code_dedup_table(magika_filter)

        already_disassembled = set()
        samples_to_check = list(in_basic_properties)
        for i in range(0, len(samples_to_check), batch_size):
            batch = samples_to_check[i:i+batch_size]
            placeholders = "','".join(batch)
            query = f"SELECT DISTINCT sha256 FROM {disassembled_table} FINAL WHERE sha256 IN ('{placeholders}')"
            result = client.query(query)
            for row in result.result_rows:
                hash_value = row[0]
                if isinstance(hash_value, bytes):
                    hash_value = hash_value.decode('utf-8')
                already_disassembled.add(hash_value)

        return not_in_basic | already_disassembled

    except Exception as e:
        print(f"[ERROR] Failed to check hashes in DB: {e}")
        return set()
    finally:
        if 'client' in locals():
            client.close()