Skip to content
Merged
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
27 changes: 27 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
16 changes: 16 additions & 0 deletions src/PgKeyValueDB/SqlExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,22 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
}
}

// Handle Contains for collections
if (node.Method.Name == nameof(Enumerable.Contains) && node.Arguments.Count == 1)
{
var collection = node.Object;
var item = node.Arguments[0];

if (collection is MemberExpression memberExpression)
{
var parentPath = BuildNestedJsonPath(memberExpression);
whereClause.Append($"jsonb_exists({parentPath}, ");
Visit(item);
whereClause.Append(")");
return node;
}
}

throw new NotSupportedException($"Method {node.Method.Name} is not supported");
}

Expand Down
86 changes: 30 additions & 56 deletions test/PgKeyValueDB.Tests/PgKeyValueDBTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,13 @@ public class PgKeyValueDBTest
{
private static PgServer pg = null!;
private static PgKeyValueDB kv = null!;
private const string TestPid = "AdvancedTest";
private static TestContext testContext = null!;
private static string connectionString = null!;

[ClassInitialize]
public static void ClassInit(TestContext context)
{
testContext = context;
IServiceCollection services = new ServiceCollection();
pg = new PgServer("16.2.0", clearWorkingDirOnStart: true, clearInstanceDirOnStop: true);
pg.Start();
connectionString = $"Host=localhost;Port={pg.PgPort};Username=postgres;Password=postgres;Database=postgres";
services.AddPgKeyValueDB($"Host=localhost;Port={pg.PgPort};Username=postgres;Password=postgres;Database=postgres", b =>
{
b.SchemaName = "pgkeyvaluetest";
Expand All @@ -77,57 +72,6 @@ public static void ClassCleanup()
pg?.Dispose();
}

private static async Task SetupTestData()
{
var users = new[]
{
new UserProfile
{
Name = "John Doe",
Age = 30,
Status = UserStatus.Active,
Role = UserRole.Admin,
PrimaryAddress = new Address
{
Street = "123 Main St",
City = "New York",
Country = "USA",
ZipCode = 10001
},
SecondaryAddress = new Address
{
Street = "456 Park Ave",
City = "Boston",
Country = "USA",
ZipCode = 02108
},
Tags = ["vip", "early-adopter"],
IsVerified = true
},
new UserProfile
{
Name = "Jane Smith",
Age = 25,
Status = UserStatus.Pending,
Role = UserRole.User,
PrimaryAddress = new Address
{
Street = "789 Oak Rd",
City = "San Francisco",
Country = "USA",
ZipCode = 94102
},
Tags = ["beta-tester"],
IsVerified = false
}
};

for (int i = 0; i < users.Length; i++)
{
await kv.UpsertAsync($"user_{i}", users[i], TestPid);
}
}

[TestMethod]
public void BasicTest()
{
Expand Down Expand Up @@ -561,4 +505,34 @@ public async Task FilterWithStringParameterTest()
Assert.IsTrue(results.Any(u => u.Name == "John Smith"));
Assert.IsTrue(results.Any(u => u.DisplayName == "Johnny"));
}

[TestMethod]
public async Task FilterByTagTest()
{
var key1 = nameof(FilterByTagTest) + "1";
var key2 = nameof(FilterByTagTest) + "2";
var pid = nameof(FilterByTagTest);

var user1 = new UserProfile
{
Name = "Alice",
Tags = new List<string> { "vip", "early-adopter" }
};

var user2 = new UserProfile
{
Name = "Bob",
Tags = new List<string> { "regular", "new-user" }
};

await kv.UpsertAsync(key1, user1, pid);
await kv.UpsertAsync(key2, user2, pid);

// This query should filter users by the "vip" tag
Expression<Func<UserProfile, bool>> expr = u => u.Tags!.Contains("vip");
var vipUsers = await kv.GetListAsync(pid, expr).ToListAsync();

Assert.AreEqual(1, vipUsers.Count);
Assert.AreEqual("Alice", vipUsers[0].Name);
}
}