Skip to content

Commit

Permalink
Allow "" to filter for explicite empty fileds
Browse files Browse the repository at this point in the history
  • Loading branch information
daschuer committed Sep 2, 2018
1 parent f3af786 commit faf9bc5
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 9 deletions.
29 changes: 27 additions & 2 deletions src/library/crate/cratestorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,17 @@ QString CrateStorage::formatQueryForTrackIdsByCrateNameLike(
escapedCrateNameLike);
}

//static
QString CrateStorage::formatQueryForTrackIdsWithCrate() {
return QString("SELECT DISTINCT %1 FROM %2 JOIN %3 ON %4=%5 ORDER BY %1").arg(
CRATETRACKSTABLE_TRACKID,
CRATE_TRACKS_TABLE,
CRATE_TABLE,
CRATETRACKSTABLE_CRATEID,
CRATETABLE_ID);
}



CrateTrackSelectResult CrateStorage::selectCrateTracksSorted(CrateId crateId) const {
FwdSqlQuery query(m_database, QString(
Expand Down Expand Up @@ -514,8 +525,6 @@ CrateSummarySelectResult CrateStorage::selectCratesWithTrackCount(const QList<Tr
}
}



CrateTrackSelectResult CrateStorage::selectTracksSortedByCrateNameLike(const QString& crateNameLike) const {
FwdSqlQuery query(m_database, QString(
"SELECT %1,%2 FROM %3 JOIN %4 ON %5 = %6 WHERE %7 LIKE :crateNameLike ORDER BY %1").arg(
Expand All @@ -535,6 +544,22 @@ CrateTrackSelectResult CrateStorage::selectTracksSortedByCrateNameLike(const QSt
}
}

CrateTrackSelectResult CrateStorage::selectAllTracksSorted() const {
FwdSqlQuery query(m_database, QString(
"SELECT %1,%2 FROM %3 JOIN %4 ON %5 = %6 ORDER BY %1").arg(
CRATETRACKSTABLE_TRACKID,
CRATETRACKSTABLE_CRATEID,
CRATE_TRACKS_TABLE,
CRATE_TABLE,
CRATETABLE_ID,
CRATETRACKSTABLE_CRATEID,
CRATETABLE_NAME));
if (query.execPrepared()) {
return CrateTrackSelectResult(std::move(query));
} else {
return CrateTrackSelectResult();
}
}

QSet<CrateId> CrateStorage::collectCrateIdsOfTracks(const QList<TrackId>& trackIds) const {
// NOTE(uklotzde): One query per track id. This could be optimized
Expand Down
2 changes: 2 additions & 0 deletions src/library/crate/cratestorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class CrateStorage: public virtual /*implements*/ SqlStorage {

QString formatQueryForTrackIdsByCrateNameLike(
const QString& crateNameLike) const; // no db access
static QString formatQueryForTrackIdsWithCrate(); // no db access
// Select the track ids of a crate or the crate ids of a track respectively.
// The results are sorted (ascending) by the target id, i.e. the id that is
// not provided for filtering. This enables the caller to perform efficient
Expand All @@ -280,6 +281,7 @@ class CrateStorage: public virtual /*implements*/ SqlStorage {
const QList<TrackId>& trackIds) const;
CrateTrackSelectResult selectTracksSortedByCrateNameLike(
const QString& crateNameLike) const;
CrateTrackSelectResult selectAllTracksSorted() const;

// Returns the set of crate ids for crates that contain any of the
// provided track ids.
Expand Down
64 changes: 63 additions & 1 deletion src/library/searchquery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,26 @@ QString TextFilterNode::toSql() const {
return concatSqlClauses(searchClauses, "OR");
}

bool NullTextFilterNode::match(const TrackPointer& pTrack) const {
for (const auto& sqlColumn: m_sqlColumns) {
QVariant value = getTrackValueForColumn(pTrack, sqlColumn);
if (!value.isValid() || !qVariantCanConvert<QString>(value)) {
continue;
}
// only use the major coloumn
return value.toString().isEmpty();
}
return false;
}

QString NullTextFilterNode::toSql() const {
for (const auto& sqlColumn: m_sqlColumns) {
// only use the major coloumn
return QString("%1 IS NULL").arg(sqlColumn);
}
return QString();
}

CrateFilterNode::CrateFilterNode(const CrateStorage* pCrateStorage,
const QString& crateNameLike)
: m_pCrateStorage(pCrateStorage),
Expand All @@ -176,7 +196,7 @@ CrateFilterNode::CrateFilterNode(const CrateStorage* pCrateStorage,
bool CrateFilterNode::match(const TrackPointer& pTrack) const {
if (!m_matchInitialized) {
CrateTrackSelectResult crateTracks(
m_pCrateStorage->selectTracksSortedByCrateNameLike(m_crateNameLike));
m_pCrateStorage->selectTracksSortedByCrateNameLike("%%"));

while (crateTracks.next()) {
m_matchingTrackIds.push_back(crateTracks.trackId());
Expand All @@ -193,9 +213,35 @@ QString CrateFilterNode::toSql() const {
m_pCrateStorage->formatQueryForTrackIdsByCrateNameLike(m_crateNameLike));
}


NoCrateFilterNode::NoCrateFilterNode(const CrateStorage* pCrateStorage)
: m_pCrateStorage(pCrateStorage),
m_matchInitialized(false) {
}

bool NoCrateFilterNode::match(const TrackPointer& pTrack) const {
if (!m_matchInitialized) {
CrateTrackSelectResult crateTracks(
m_pCrateStorage->selectAllTracksSorted());

while (crateTracks.next()) {
m_matchingTrackIds.push_back(crateTracks.trackId());
}

m_matchInitialized = true;
}

return !std::binary_search(m_matchingTrackIds.begin(), m_matchingTrackIds.end(), pTrack->getId());
}

QString NoCrateFilterNode::toSql() const {
return QString("id NOT IN (%1)").arg(CrateStorage::formatQueryForTrackIdsWithCrate());
}

NumericFilterNode::NumericFilterNode(const QStringList& sqlColumns)
: m_sqlColumns(sqlColumns),
m_bOperatorQuery(false),
m_bNullQuery(false),
m_operator("="),
m_dOperatorArgument(0.0),
m_bRangeQuery(false),
Expand All @@ -210,6 +256,11 @@ NumericFilterNode::NumericFilterNode(
}

void NumericFilterNode::init(QString argument) {
if (argument == "\"\"") {
m_bNullQuery = true;
return;
}

QRegExp operatorMatcher("^(>|>=|=|<|<=)(.*)$");
if (operatorMatcher.indexIn(argument) != -1) {
m_operator = operatorMatcher.cap(1);
Expand Down Expand Up @@ -244,6 +295,9 @@ bool NumericFilterNode::match(const TrackPointer& pTrack) const {
for (const auto& sqlColumn: m_sqlColumns) {
QVariant value = getTrackValueForColumn(pTrack, sqlColumn);
if (!value.isValid() || !qVariantCanConvert<double>(value)) {
if (m_bNullQuery) {
return true;
}
continue;
}

Expand All @@ -265,6 +319,14 @@ bool NumericFilterNode::match(const TrackPointer& pTrack) const {
}

QString NumericFilterNode::toSql() const {
if (m_bNullQuery) {
for (const auto& sqlColumn: m_sqlColumns) {
// only use the major coloumn
return QString("%1 IS NULL").arg(sqlColumn);
}
return QString();
}

if (m_bOperatorQuery) {
QStringList searchClauses;
for (const auto& sqlColumn: m_sqlColumns) {
Expand Down
32 changes: 32 additions & 0 deletions src/library/searchquery.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ class TextFilterNode : public QueryNode {
QString m_argument;
};

class NullTextFilterNode : public QueryNode {
public:
NullTextFilterNode(const QSqlDatabase& database,
const QStringList& sqlColumns)
: m_database(database),
m_sqlColumns(sqlColumns) {
}

bool match(const TrackPointer& pTrack) const override;
QString toSql() const override;

private:
QSqlDatabase m_database;
QStringList m_sqlColumns;
};


class CrateFilterNode : public QueryNode {
public:
CrateFilterNode(const CrateStorage* pCrateStorage,
Expand All @@ -106,6 +123,20 @@ class CrateFilterNode : public QueryNode {
mutable std::vector<TrackId> m_matchingTrackIds;
};

class NoCrateFilterNode : public QueryNode {
public:
NoCrateFilterNode(const CrateStorage* pCrateStorage);

bool match(const TrackPointer& pTrack) const override;
QString toSql() const override;

private:
const CrateStorage* m_pCrateStorage;
QString m_crateNameLike;
mutable bool m_matchInitialized;
mutable std::vector<TrackId> m_matchingTrackIds;
};

class NumericFilterNode : public QueryNode {
public:
NumericFilterNode(const QStringList& sqlColumns, const QString& argument);
Expand All @@ -128,6 +159,7 @@ class NumericFilterNode : public QueryNode {

QStringList m_sqlColumns;
bool m_bOperatorQuery;
bool m_bNullQuery;
QString m_operator;
double m_dOperatorArgument;
bool m_bRangeQuery;
Expand Down
34 changes: 28 additions & 6 deletions src/library/searchqueryparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ QString SearchQueryParser::getTextArgument(QString argument,
QStringList* tokens) const {
// If the argument is empty, assume the user placed a space after an
// advanced search command. Consume another token and treat that as the
// argument. TODO(XXX) support quoted search phrases as arguments
// argument.
argument = argument.trimmed();
if (argument.length() == 0) {
if (tokens->length() > 0) {
Expand Down Expand Up @@ -99,8 +99,14 @@ QString SearchQueryParser::getTextArgument(QString argument,
tokens->push_front(remaining);
}

// Slice off the quote and everything after.
argument = argument.left(quote_index);
if (quote_index == 0) {
// We have found an explicit empty string ""
// return it as "" to distingish it from anunfinished empty string
argument = "\"\"";
} else {
// Slice off the quote and everything after.
argument = argument.left(quote_index);
}
}

return argument;
Expand All @@ -125,7 +131,18 @@ void SearchQueryParser::parseTokens(QStringList tokens,
QString argument = getTextArgument(
m_textFilterMatcher.cap(2), &tokens).trimmed();

if (!argument.isEmpty()) {
if (argument == "\"\"") {
qDebug() << "argument explicit empty";
if (field == "crate") {
pNode = std::make_unique<NoCrateFilterNode>(
&m_pTrackCollection->crates());
qDebug() << pNode->toSql();
} else {
pNode = std::make_unique<NullTextFilterNode>(
m_pTrackCollection->database(), m_fieldToSqlColumns[field]);
qDebug() << pNode->toSql();
}
} else if (!argument.isEmpty()) {
if (field == "crate") {
pNode = std::make_unique<CrateFilterNode>(
&m_pTrackCollection->crates(), argument);
Expand Down Expand Up @@ -153,8 +170,13 @@ void SearchQueryParser::parseTokens(QStringList tokens,
mixxx::track::io::key::ChromaticKey key =
KeyUtils::guessKeyFromText(argument);
if (key == mixxx::track::io::key::INVALID) {
pNode = std::make_unique<TextFilterNode>(
m_pTrackCollection->database(), m_fieldToSqlColumns[field], argument);
if (argument == "\"\"") {
pNode = std::make_unique<NullTextFilterNode>(
m_pTrackCollection->database(), m_fieldToSqlColumns[field]);
} else {
pNode = std::make_unique<TextFilterNode>(
m_pTrackCollection->database(), m_fieldToSqlColumns[field], argument);
}
} else {
pNode = std::make_unique<KeyFilterNode>(key, fuzzy);
}
Expand Down

0 comments on commit faf9bc5

Please sign in to comment.