Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
}

func testMultipleInOps() async throws {
try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
"Skip this test if running against production.")

let collRef = collectionRef(
withDocuments: ["doc1": ["a": 1, "b": 0],
"doc2": ["b": 1],
Expand Down
33 changes: 32 additions & 1 deletion Firestore/Swift/Tests/Integration/QueryToPipelineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,11 @@ class QueryToPipelineTests: FSTIntegrationTestCase {
}

func testSupportsNeqNan() async throws {
try XCTSkipIf(
FSTIntegrationTestCase.isRunningAgainstEmulator(),
"Skipping test because the emulator's behavior deviates from the expected outcome."
)

let collRef = collectionRef(withDocuments: [
"1": ["foo": 1, "bar": Double.nan],
"2": ["foo": 2, "bar": 1],
Expand All @@ -579,6 +584,11 @@ class QueryToPipelineTests: FSTIntegrationTestCase {
}

func testSupportsEqNull() async throws {
try XCTSkipIf(
FSTIntegrationTestCase.isRunningAgainstEmulator(),
"Skipping test because the emulator's behavior deviates from the expected outcome."
)

let collRef = collectionRef(withDocuments: [
"1": ["foo": 1, "bar": NSNull()],
"2": ["foo": 2, "bar": 1],
Expand All @@ -593,6 +603,11 @@ class QueryToPipelineTests: FSTIntegrationTestCase {
}

func testSupportsNeqNull() async throws {
try XCTSkipIf(
FSTIntegrationTestCase.isRunningAgainstEmulator(),
"Skipping test because the emulator's behavior deviates from the expected outcome."
)

let collRef = collectionRef(withDocuments: [
"1": ["foo": 1, "bar": NSNull()],
"2": ["foo": 2, "bar": 1],
Expand Down Expand Up @@ -701,6 +716,11 @@ class QueryToPipelineTests: FSTIntegrationTestCase {
}

func testSupportsNotInWith1() async throws {
try XCTSkipIf(
FSTIntegrationTestCase.isRunningAgainstEmulator(),
"Skipping test because the emulator's behavior deviates from the expected outcome."
)

let collRef = collectionRef(withDocuments: [
"1": ["foo": 1, "bar": 2],
"2": ["foo": 2],
Expand All @@ -712,7 +732,18 @@ class QueryToPipelineTests: FSTIntegrationTestCase {
let pipeline = db.pipeline().create(from: query)
let snapshot = try await pipeline.execute()

verifyResults(snapshot, [["foo": 3, "bar": 10]])
switch FSTIntegrationTestCase.backendEdition() {
case .standard:
// In Standard, `NOT_IN` requires the field to exist.
// So document "2" (with no "bar" field) is filtered out.
verifyResults(snapshot, [["foo": 3, "bar": 10]])
case .enterprise:
// In Enterprise, `NOT_IN` does not require the field to exist.
// So document "2" (with no "bar" field) is included.
verifyResults(snapshot, [["foo": 2], ["foo": 3, "bar": 10]])
@unknown default:
XCTFail("Unknown backend edition")
}
}

func testSupportsOrOperator() async throws {
Expand Down
24 changes: 19 additions & 5 deletions Firestore/core/src/core/pipeline_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@ std::shared_ptr<api::Expr> ToPipelineBooleanExpr(const Filter& filter) {
comparison_expr = std::make_shared<api::FunctionExpr>(
func_name,
std::vector<std::shared_ptr<api::Expr>>{api_field, api_constant});
if (op == FieldFilter::Operator::NotIn ||
op == FieldFilter::Operator::NotEqual) {
return comparison_expr;
}
return std::make_shared<api::FunctionExpr>(
"and",
std::vector<std::shared_ptr<api::Expr>>{exists_expr, comparison_expr});
Expand Down Expand Up @@ -703,16 +707,26 @@ std::vector<std::shared_ptr<api::EvaluableStage>> ToPipelineStages(
if (!query_order_bys.empty()) {
std::vector<std::shared_ptr<api::Expr>> exists_exprs;
exists_exprs.reserve(query_order_bys.size());
const auto inequality_fields = query.InequalityFilterFields();
for (const auto& core_order_by : query_order_bys) {
if (inequality_fields.find(core_order_by.field()) !=
inequality_fields.end()) {
Comment thread
MarkDuckworth marked this conversation as resolved.
Outdated
continue;
}
if (core_order_by.field().IsKeyFieldPath()) {
continue;
}
exists_exprs.push_back(std::make_shared<api::FunctionExpr>(
"exists", std::vector<std::shared_ptr<api::Expr>>{
std::make_shared<api::Field>(core_order_by.field())}));
}
if (exists_exprs.size() == 1) {
stages.push_back(std::make_shared<api::Where>(exists_exprs[0]));
} else {
stages.push_back(std::make_shared<api::Where>(
std::make_shared<api::FunctionExpr>("and", exists_exprs)));
if (!exists_exprs.empty()) {
if (exists_exprs.size() == 1) {
stages.push_back(std::make_shared<api::Where>(exists_exprs[0]));
} else {
stages.push_back(std::make_shared<api::Where>(
std::make_shared<api::FunctionExpr>("and", exists_exprs)));
}
}
}

Expand Down
Loading