From c577716b3178d80ccabe6ed4aca90d81b60b2693 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Mon, 21 Aug 2023 14:15:38 -0600 Subject: [PATCH] feat: prep work for fuzzy matching --- include/flox/pkgdb/read.hh | 9 +++++ src/pkgdb/query-builder.cc | 5 +++ src/pkgdb/read.cc | 43 ++++++++++++++++++++++++ tests/read.cc | 67 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 tests/read.cc diff --git a/include/flox/pkgdb/read.hh b/include/flox/pkgdb/read.hh index a4503167..4713e50b 100644 --- a/include/flox/pkgdb/read.hh +++ b/include/flox/pkgdb/read.hh @@ -23,6 +23,7 @@ #include "flox/core/exceptions.hh" #include "flox/core/types.hh" #include "flox/core/command.hh" +#include "flox/package.hh" /* -------------------------------------------------------------------------- */ @@ -251,6 +252,14 @@ class PkgDbReadOnly { }; /* End class `PkgDbReadOnly' */ +/** + * Calculate a distance that can be used to order packages by how close they + * are to a match string. + * @param Package The Package to + * @param match String to look for in Package's fields. + * @return Distance between pkg and match. +*/ +std::optional distanceFromMatch( Package & pkg, std::string match ); /* -------------------------------------------------------------------------- */ diff --git a/src/pkgdb/query-builder.cc b/src/pkgdb/query-builder.cc index 3c943227..ddfe68d9 100644 --- a/src/pkgdb/query-builder.cc +++ b/src/pkgdb/query-builder.cc @@ -37,6 +37,11 @@ buildPkgQuery( PkgQueryArgs && params ) { q.where( column( "pname" ) == ":pname" ); } + + if ( params.match.has_value() && !params.match->empty() ) + { + q.where( "( name LIKE '%:match%' ) OR ( description LIKE '%:match%' )" ); + } if ( params.version.has_value() ) { diff --git a/src/pkgdb/read.cc b/src/pkgdb/read.cc index 672275ba..75c4afda 100644 --- a/src/pkgdb/read.cc +++ b/src/pkgdb/read.cc @@ -319,6 +319,49 @@ PkgDbReadOnly::getDescendantAttrSets( row_id root ) } +/* -------------------------------------------------------------------------- */ + + std::optional +distanceFromMatch( Package & pkg, std::string match ) +{ + if ( match.empty() ) { return std::nullopt; } + + std::string pname = pkg.getPname(); + // TODO match on attrName. That's not currently possible because attrName is + // meaningful for flakes, but for catalogs, only the attrName of parent is + // meaningful (attrName is a version string). + + // Don't give description any weight if pname matches exactly. It's not + // especially meaningful if a description mentions its own name. + if ( pname == match ) + { + // pname matches exactly + return 0; + } + + bool pnameMatches = (pname.find(match) != std::string::npos); + auto description = pkg.getDescription(); + bool descriptionMatches = (description.has_value() && description->find(match) != std::string::npos); + + if ( pnameMatches ) { + if ( descriptionMatches ) + { + // pname and description match + return 1; + } + // only pname matches + return 2; + } + + if ( descriptionMatches ) + { + // only description matches + return 3; + } + // nothing matches + return 4; +} + /* -------------------------------------------------------------------------- */ } /* End Namespace `flox::pkgdb' */ diff --git a/tests/read.cc b/tests/read.cc new file mode 100644 index 00000000..d1263de8 --- /dev/null +++ b/tests/read.cc @@ -0,0 +1,67 @@ +/* ========================================================================== * + * + * @file read.cc + * + * @brief Tests for `read.cc`. + * + * + * -------------------------------------------------------------------------- */ + +#include + +#include "flox/pkgdb/read.hh" +#include "test.hh" +#include "flox/flake-package.hh" + +/* -------------------------------------------------------------------------- */ + +/** + * Test ability to add `AttrSet` rows. + * This test should run before all others since it essentially expects + * `AttrSets` to be empty. + */ + bool +test_distanceFromMatch() +{ + // auto pkg = flox::FlakePackage(); + // flox::pkgdb::distanceFromMatch(pkg, "match"); + return true; +} + +/* ========================================================================== */ + + int +main( int argc, char * argv[] ) +{ + int ec = EXIT_SUCCESS; +# define RUN_TEST( ... ) _RUN_TEST( ec, __VA_ARGS__ ) + +/* -------------------------------------------------------------------------- */ + + nix::Verbosity verbosity; + if ( ( 1 < argc ) && ( std::string_view( argv[1] ) == "-v" ) ) + { + verbosity = nix::lvlDebug; + } + else + { + verbosity = nix::lvlWarn; + } + +/* -------------------------------------------------------------------------- */ + + { + RUN_TEST( distanceFromMatch ); + } + +/* -------------------------------------------------------------------------- */ + + return ec; +} + + +/* -------------------------------------------------------------------------- * + * + * + * + * ========================================================================== */