{"id":20816699,"url":"https://github.com/moritzrinow/labelkit-net","last_synced_at":"2025-10-19T15:42:24.825Z","repository":{"id":237280554,"uuid":"790947301","full_name":"moritzrinow/labelkit-net","owner":"moritzrinow","description":".NET toolkit for parsing and using kubernetes-like label-selectors including support for EFCore.","archived":false,"fork":false,"pushed_at":"2024-05-05T14:23:21.000Z","size":35,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-21T04:58:07.478Z","etag":null,"topics":["efcore","label-selector","labels","linq","mysql","postgres","sqlite","sqlserver"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moritzrinow.png","metadata":{"files":{"readme":"README.md","changelog":"changelog/release-notes-0.1.0.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-04-23T20:17:04.000Z","updated_at":"2024-05-05T14:12:57.000Z","dependencies_parsed_at":"2024-04-30T17:48:04.225Z","dependency_job_id":"d2a66e2d-721b-415b-9fac-eeba00971f56","html_url":"https://github.com/moritzrinow/labelkit-net","commit_stats":null,"previous_names":["moritzrinow/labelkit-net"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/moritzrinow/labelkit-net","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moritzrinow%2Flabelkit-net","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moritzrinow%2Flabelkit-net/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moritzrinow%2Flabelkit-net/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moritzrinow%2Flabelkit-net/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moritzrinow","download_url":"https://codeload.github.com/moritzrinow/labelkit-net/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moritzrinow%2Flabelkit-net/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279840812,"owners_count":26234202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-19T02:00:07.647Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["efcore","label-selector","labels","linq","mysql","postgres","sqlite","sqlserver"],"created_at":"2024-11-17T21:36:20.563Z","updated_at":"2025-10-19T15:42:24.786Z","avatar_url":"https://github.com/moritzrinow.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LabelKit\n\nLabelKit is a .NET toolkit for parsing [kubernetes-like label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) and using them to build filters, including query-expressions for EFCore (PostgreSQL, MySql, SqlServer, Sqlite).\nLabels are name/value pairs that can be represented as dictionaries (name-\u003evalue) or simple string collections (name+delimiter+value).\n\n- [Packages](#packages)\n- [Examples](#examples)\n  - [EFCore PostgreSQL](#efcore-postgresql)\n  - [Label-Selectors](#label-selectors)\n  - [Matching Labels Offline](#matching-labels-offline)\n- [Expressions (EFCore)](#expressions-efcore)\n  - [Supported EFCore Providers](#supported-efcore-providers)\n  - [Provided Expression-Builders](#provided-expression-builders)\n  - [Filter IQueryables](#filter-iqueryables)\n\n## Packages\n\nThe majority of packages support netstandard2.0.\n\nThe following NuGet packages are provided:\n\n- [LabelKit](https://www.nuget.org/packages/LabelKit/)\n  - You need to reference structured label-selectors.\n- [LabelKit.Parser](https://www.nuget.org/packages/LabelKit.Parser/)\n  - You need to parse raw label-selectors.\n- [LabelKit.Expressions](https://www.nuget.org/packages/LabelKit.Expressions/)\n  - You need to build expressions and filter queries. See [here](#expressions-efcore).\n- [LabelKit.EFCore.PostgreSQL](https://www.nuget.org/packages/LabelKit.EFCore.PostgreSQL/)\n  - You need to filter EFCore-PostgreSQL queries. See [here](#expressions-efcore).\n- [LabelKit.EFCore.Pomelo.MySql](https://www.nuget.org/packages/LabelKit.EFCore.Pomelo.MySql/)\n  - You need to filter EFCore-MySql queries. See [here](#expressions-efcore).\n\n## Examples\n\n### EFCore PostgreSQL:\n\n```csharp\npublic class Entity\n{\n  // Stored as JSONB\n  public Dictionary\u003cstring, string\u003e Labels { get; set; }\n}\n```\n\n`dotnet add package LabelKit.Parser`\n\nLabelKit.Parser offers a default parser built with [Pidgin](https://github.com/benjamin-hodgson/Pidgin).\n\nThe parser is able to parse raw label-selectors adhering to the [kubernetes syntax](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors).\n\n```csharp\nusing LabelKit;\n\nvar selector = LabelSelectorParser.Parse(\n  \"label1 = value, label2 = value, label3 in (value1, value2)\");\n```\n\n`dotnet add package LabelKit.EFCore.PostgreSQL`\n\n```csharp\nvar expressionBuilder = NpgsqlLabelSelectorExpressionBuilders.Jsonb\u003cDictionary\u003cstring, string\u003e\u003e();\n\nvar entities = await dbContext.Set\u003cEntity\u003e()\n  .MatchLabels(e =\u003e e.Labels, selector, expressionBuilder)\n  .ToListAsync();\n```\n\nExecuted SQL:\n```postgresql\n-- @__json_1='{\"label1\":\"value\",\"label2\":\"value\"}' (DbType = Object)\n-- @__Format_2='{\"label3\":\"value1\"}' (DbType = Object)\n-- @__Format_3='{\"label3\":\"value2\"}' (DbType = Object)\nSELECT t.\"Id\", t.\"Labels\"\nFROM \"TestEntity\" AS t\nWHERE t.\"Labels\" @\u003e @__json_1 AND (t.\"Labels\" @\u003e @__Format_2 OR t.\"Labels\" @\u003e @__Format_3)\n```\n\n### Label-Selectors\n\nLabel-selectors can be easily created and extended...\n\n```csharp\nvar selector = LabelSelector.New()\n  .Match(\"label1\").Exact(\"value\")\n  .Match(\"label2\").Not(\"value\")\n  .Match(\"label3\").In(\"value1\", \"value2\")\n  .Match(\"label4\").NotIn(\"value1\", \"value2\")\n  .Match(\"label5\").Exists()\n  .Match(\"label6\").NotExists();\n```\n\nThey can be merged...\n\n```csharp\nvar selector1 = LabelSelector.New()\n  .Match(\"label1\").Exact(\"value\");\n\nvar selector2 = LabelSelector.New()\n  .Match(\"label2\").Exact(\"value\");\n\n// Contains a fully copy of all expressions\nvar merged = selector1.Merge(selector2);\n\n// You can merge an arbitrary number of selectors\n\nmerged = LabelSelector.Merge(selector1, selector2, ...);\n\n```\n\nThey can be rendered...\n\n```csharp\n// label1 = value, label2 = value\nmerged.ToString();\n```\n\n### Matching Labels Offline\n\nYou can also use label-selectors offline without any database interaction.\n\n`dotnet add package LabelKit`\n\n```csharp\nvar selector = LabelSelector.New()\n  .Match(\"label1\").Exact(\"value\")\n  .Match(\"label2\").Exact(\"value\");\n\nstring[] labels = [ \"label1:value\", \"label2:value\" ];\n\n// Default delimiter is ':'\n// -\u003e true\nvar doesMatch = selector.Matches(labels);\n```\n\nThe same can be done with dictionary-like labels:\n```csharp\nvar selector = LabelSelector.New()\n  .Match(\"label1\").Exact(\"value\")\n  .Match(\"label2\").Exact(\"value\");\n\nvar labels = new Dictionary\u003cstring, string()\n{\n  [\"label1\"] = \"value\",\n  [\"label2\"] = \"value\"\n};\n\n// -\u003e true\nvar doesMatch = selector.Matches(labels);\n```\n\n\u003e [!TIP]\n\u003e \n\u003e Any component implementing **ILabelSelector** (meaning it can provide a collection of selector-expressions) can be used to match offline.\n\n## Expressions (EFCore)\n\nLabelKit supports infrastructure for building expression-trees that can be used to filter IQueryables.\nComponents implementing **ILabelSelectorExpressionBuilder** are responsible for creating **Expressions** from label-selectors.\nDifferent expression-builders are needed for different scenarios.\n\nExample: If your labels are stored as JSONB (PostgreSQL), the resulting SQL query has to be\nvastly different from if they were stored as an array. Therefore, you need a different expression.\n\n### Supported EFCore Providers\n\n- [PostgreSQL](https://github.com/npgsql/efcore.pg) (\u003e=5.0.5)\n  - Supports labels stored as [JSONB](https://www.npgsql.org/efcore/mapping/json.html#traditional-poco-mapping-deprecated) (\"name\": \"value\")\n  - Supports labels stored as [primitive-collection](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) of strings (\"name{separator}value\").\n- [MySql](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql) (\u003e=3.2.0)\n  - Supports labels stored as [JSON](https://dev.mysql.com/doc/refman/8.0/en/json.html) (\"name\": \"value\")\n  - Supports labels stored as [primitive-collection](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) of strings (\"name{separator}value\").\n    - Disabled by default in the provider due to [this issue here](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1791).\n- [SqlServer](https://github.com/dotnet/efcore) (\u003e=8.0.0)\n  - Supports labels stored as [primitive-collection](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) of strings (\"name{separator}value\").\n- [Sqlite](https://github.com/dotnet/efcore) (\u003e=8.0.0)\n  - Supports labels stored as [primitive-collection](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) of strings (\"name{separator}value\").\n- Any other EFCore provider supporting primitive-collections\n  - Supports labels stored as [primitive-collection](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) of strings (\"name{separator}value\").\n\n### Provided Expression-Builders\n\n- `NpgsqlJsonbLabelSelectorExpressionBuilder` (`NpgsqlLabelSelectorExpressionBuilders.Jsonb()`)\n  - Builds expression specifically suited for PostgreSQL JSONB columns.\n- `MySqlJsonLabelSelectorExpressionBuilder` (`MySqlLabelSelectorExpressionBuilders.Json()`)\n  - Builds expression specifically suited for MySql JSON columns.\n- `CollectionLabelSelectorExpressionBuilder` (`LabelSelectorExpressionBuilders.Collection()`)\n  - Builds generic expression suitable for any collection of strings.\n  - Supported by PostgreSQL, SqlServer, Sqlite (and MySql).\n  - Available in package `LabelKit.Expressions`.\n\n\u003e [!IMPORTANT]\n\u003e \n\u003e `CollectionLabelSelectorExpressionBuilder` produces expressions that should be translatable to SQL by EFCore\n\u003e providers supporting [primitive-collections](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections).\n\u003e However, you should also be able to use those expressions offline (compiled).\n\n### Filter IQueryables\n\n`dotnet add package LabelKit.Expressions`\n\n```csharp\nusing LabelKit;\n\nvar expressionBuilder = LabelSelectorExpressionBuilders.Collection\u003cstring[]\u003e();\n\n// e =\u003e e.Labels is an expression representing the labels to match.\n// You can also mark your entity as ILabelledEntity to avoid having to specify this every time.\nvar entities = await dbContext.Set\u003cEntity\u003e()\n  .MatchLabels(e =\u003e e.Labels,\n    selector =\u003e selector\n        .Match(\"label1\").Exact(\"value\")\n        .Match(\"label2\").Exact(\"value\")\n    , expressionBuilder)\n  .ToListAsync();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoritzrinow%2Flabelkit-net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoritzrinow%2Flabelkit-net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoritzrinow%2Flabelkit-net/lists"}