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
2 changes: 2 additions & 0 deletions src/fdb5/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ if( HAVE_TOCFDB )
toc/FieldRef.h
toc/FileSpaceHandler.cc
toc/FileSpaceHandler.h
toc/FileSpaceSelector.cc
toc/FileSpaceSelector.h
toc/FileSpace.cc
toc/FileSpace.h
toc/ExpverFileSpaceHandler.cc
Expand Down
59 changes: 49 additions & 10 deletions src/fdb5/toc/FileSpace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#include "eckit/utils/StringTools.h"

#include "eckit/exception/Exceptions.h"
#include "eckit/filesystem/FileSpaceStrategies.h"
#include "eckit/os/BackTrace.h"

#include "fdb5/LibFdb5.h"
#include "fdb5/database/Key.h"
Expand All @@ -31,13 +29,54 @@ struct VectorPrintSelector<fdb5::Root> {
};
} // namespace eckit

//----------------------------------------------------------------------------------------------------------------------

namespace fdb5 {

namespace {

metkit::mars::Matcher buildMatcher(const eckit::LocalConfiguration& match) {
std::map<std::string, eckit::Regex> regexMap;
for (const auto& keyword : match.keys()) {
std::string pattern;
if (match.isList(keyword)) {
auto values = match.getStringVector(keyword);
if (values.empty()) {
std::ostringstream oss;
oss << "FileSpace match: keyword '" << keyword << "' has no values";
throw eckit::UserError(oss.str(), Here());
}
pattern = "^(";
const char* sep = "";
for (const auto& v : values) {
pattern += sep;
pattern += v;
sep = "|";
}
pattern += ")$";
}
else {
pattern = "^(" + match.getString(keyword) + ")$";
}
regexMap.emplace(keyword, eckit::Regex(pattern));
}

return metkit::mars::Matcher(std::move(regexMap), metkit::mars::Matcher::Policy::All);
}

std::variant<RegexSelector, MatchSelector> makeSelector(const eckit::LocalConfiguration& space) {
if (space.has("match")) {
return MatchSelector{buildMatcher(space.getSubConfiguration("match"))};
}
return RegexSelector{eckit::Regex{space.getString("regex", ".*")}};
}

} // namespace

//----------------------------------------------------------------------------------------------------------------------

FileSpace::FileSpace(const std::string& name, const std::string& re, const std::string& handler,
const std::vector<Root>& roots) :
name_(name), handler_(handler), re_(re), roots_(roots) {}
FileSpace::FileSpace(const std::string& name, const eckit::LocalConfiguration& space, const std::vector<Root>& roots) :
name_{name}, handler_{space.getString("handler", "Default")}, selector_{makeSelector(space)}, roots_{roots} {}

TocPath FileSpace::filesystem(const Config& config, const Key& key, const eckit::PathName& db) const {
// check that the database isn't present already
Expand All @@ -49,7 +88,6 @@ TocPath FileSpace::filesystem(const Config& config, const Key& key, const eckit:
return existingDB;
}

LOG_DEBUG_LIB(LibFdb5) << "FDB for key " << key << " not found, selecting a root" << std::endl;
LOG_DEBUG_LIB(LibFdb5) << "FDB for key " << key << " not found, selecting a root" << std::endl;

return TocPath{FileSpaceHandler::lookup(handler_, config).selectFileSystem(key, *this) / db, ControlIdentifiers{}};
Expand Down Expand Up @@ -81,12 +119,11 @@ void FileSpace::enabled(const ControlIdentifier& controlIdentifier, eckit::Strin
}
}

bool FileSpace::match(const std::string& s) const {
return re_.match(s);
bool FileSpace::match(const Key& key) const {
return std::visit([&](const auto& selector) { return selector.match(key); }, selector_);
}

eckit::PathName getFullDB(const eckit::PathName& path, const std::string& db) {

static bool searchCaseSensitiveDB =
eckit::Resource<bool>("fdbSearchCaseSensitiveDB;$FDB_SEARCH_CASESENSITIVE_DB", true);

Expand Down Expand Up @@ -158,7 +195,9 @@ bool FileSpace::existsDB(const Key& key, const eckit::PathName& db, TocPath& exi

void FileSpace::print(std::ostream& out) const {
out << "FileSpace("
<< "name=" << name_ << ",handler=" << handler_ << ",regex=" << re_ << ",roots=" << roots_ << ")";
<< "name=" << name_ << ",handler=" << handler_;
std::visit([&out](const auto& s) { out << ",selector=" << s; }, selector_);
out << ",roots=" << roots_ << ")";
}

std::vector<eckit::PathName> FileSpace::roots() const {
Expand Down
13 changes: 7 additions & 6 deletions src/fdb5/toc/FileSpace.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@

#include <iosfwd>
#include <string>
#include <variant>
#include <vector>

#include "eckit/config/LocalConfiguration.h"
#include "eckit/types/Types.h"
#include "eckit/utils/Regex.h"

#include "fdb5/api/helpers/ControlIterator.h"
#include "fdb5/toc/FileSpaceSelector.h"
#include "fdb5/toc/Root.h"

namespace fdb5 {

class Config;
class FileSpaceHandler;
class Key;

//----------------------------------------------------------------------------------------------------------------------

Expand All @@ -44,8 +46,7 @@ class FileSpace {

public: // methods

FileSpace(const std::string& name, const std::string& re, const std::string& handler,
const std::vector<Root>& roots);
FileSpace(const std::string& name, const eckit::LocalConfiguration& space, const std::vector<Root>& roots);

/// Selects the filesystem from where this Key will be inserted
/// @note This method must be idempotent -- it returns always the same value after the first call
Expand All @@ -57,7 +58,7 @@ class FileSpace {
void enabled(const ControlIdentifier& controlIdentifier, eckit::StringSet&) const;
std::vector<eckit::PathName> enabled(const ControlIdentifier& controlIdentifier) const;

bool match(const std::string& s) const;
bool match(const Key& key) const;

friend std::ostream& operator<<(std::ostream& s, const FileSpace& x) {
x.print(s);
Expand All @@ -80,7 +81,7 @@ class FileSpace {

std::string handler_;

eckit::Regex re_;
std::variant<RegexSelector, MatchSelector> selector_;

RootVec roots_;
};
Expand Down
73 changes: 73 additions & 0 deletions src/fdb5/toc/FileSpaceSelector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* (C) Copyright 2026- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/


#include "fdb5/toc/FileSpaceSelector.h"

#include "fdb5/database/Key.h"

using metkit::mars::Matcher;

namespace fdb5 {

//----------------------------------------------------------------------------------------------------------------------
// Helpers

namespace {

/// Adapts fdb5::Key to metkit::mars::RequestLike for use with Matcher.
/// Empty values are treated as absent.
class PartialKeyAdapter : public metkit::mars::RequestLike {
public:

explicit PartialKeyAdapter(const Key& key) : key_(key) {}

std::optional<values_t> get(const std::string& keyword) const override {
const auto [it, found] = key_.find(keyword);
if (!found || it->second.empty()) {
return std::nullopt;
}
return std::cref(it->second);
}

private:

const Key& key_;
};

} // namespace

//----------------------------------------------------------------------------------------------------------------------

RegexSelector::RegexSelector(eckit::Regex regex) : regex_{std::move(regex)} {}

bool RegexSelector::match(const Key& key) const {
return regex_.match(key.valuesToString());
}

void RegexSelector::print(std::ostream& out) const {
out << regex_;
}

//----------------------------------------------------------------------------------------------------------------------

MatchSelector::MatchSelector(metkit::mars::Matcher matcher) : matcher_{std::move(matcher)} {}

bool MatchSelector::match(const Key& key) const {
return matcher_.match(PartialKeyAdapter(key), Matcher::MatchOnMissing);
}

void MatchSelector::print(std::ostream& out) const {
out << matcher_;
}

//----------------------------------------------------------------------------------------------------------------------

} // namespace fdb5
84 changes: 84 additions & 0 deletions src/fdb5/toc/FileSpaceSelector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* (C) Copyright 2026- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/


/// @author Metin Cakircali
/// @date June 2026

#pragma once

#include <iosfwd>

#include "eckit/utils/Regex.h"
#include "metkit/mars/Matcher.h"

namespace fdb5 {

class Key;

//----------------------------------------------------------------------------------------------------------------------

class FileSpaceSelector {
public: // methods

virtual ~FileSpaceSelector() = default;

virtual bool match(const Key& key) const = 0;

private: // methods

virtual void print(std::ostream& out) const = 0;

friend std::ostream& operator<<(std::ostream& out, const FileSpaceSelector& selector) {
selector.print(out);
return out;
}
};

//----------------------------------------------------------------------------------------------------------------------

/// uses a regex to match against the serialised key.
class RegexSelector : public FileSpaceSelector {
public: // methods

RegexSelector(eckit::Regex regex);

bool match(const Key& key) const override;

private: // methods

void print(std::ostream& out) const override;

private: // members

eckit::Regex regex_;
};

//----------------------------------------------------------------------------------------------------------------------
// uses a metkit::mars::Matcher to match against the fdb5::Key.
class MatchSelector : public FileSpaceSelector {
public: // methods

MatchSelector(metkit::mars::Matcher matcher);

bool match(const Key& key) const override;

private: // methods

void print(std::ostream& out) const override;

private: // members

metkit::mars::Matcher matcher_;
};

//----------------------------------------------------------------------------------------------------------------------

} // namespace fdb5
Loading
Loading