Skip to content

Commit 1b5e41c

Browse files
committed
Add "hstore" documentation
1 parent da04235 commit 1b5e41c

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# `hstore` Type Mapping
2+
3+
PostgreSQL has the unique feature of supporting [*hstore data types*](https://www.postgresql.org/docs/current/static/hstore.html). This allows you to conveniently and efficiently store a set of string key/value pairs in a single column, where in other database you'd typically resort to storing the values as a JSON string or defining another table with a one-to-many relationship.
4+
5+
## Mapping hstores
6+
7+
Define a `Dictionary<string, string>` or `ImmutableDictionary<string, string>` property on an entity and it will automatically use the `hstore` value type.
8+
The next migration you add will also enable the `hstore` extension in PostgreSQL for you automatically.
9+
10+
The `hstore` type does also support nullable columns so you can define a `Dictionary<string, string>?` or `ImmutableDictionary<string, string>?` to make the column nullable.
11+
12+
```csharp
13+
public class Post
14+
{
15+
public int Id { get; set; }
16+
public Dictionary<string, string> Metadata { get; set; } = new();
17+
public ImmutableDictionary<string, string> Tags { get; set; } = ImmutableDictionary<string, string>.Empty;
18+
public Dictionary<string, string>? Events { get; set; }
19+
public ImmutableDictionary<string, string>? Labels { get; set; }
20+
}
21+
```
22+
23+
The provider will create `hstore` columns for the above four properties, and will properly detect changes in them.
24+
25+
If you load a `Dictionary<string, string>` and change one of its elements, calling `SaveChanges` will automatically update the row in the database accordingly.
26+
As an `ImmutableDictionary<string, string>` is immutable, you must `set` the property to a new value when adding/updating values and then call `SaveChanges` to update the database row.
27+
28+
```csharp
29+
post.Tags = post.Tags.Add("a key", "a value");
30+
```
31+
32+
## Operation translation
33+
34+
The provider can also translate CLR Dictionary operations to the corresponding SQL operation; this allows you to efficiently work with `hstore`s by evaluating operations in the database and avoids pulling all the data. The following table lists the Dictionary operations that currently get translated; all these translations work both for .NET `Dictionary<string, string>` and `ImmutableDictionary<string, string>` unless specifically noted. If you run into a missing operation, please open an issue.
35+
36+
.NET | SQL | Notes
37+
-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------
38+
store[key] | [store -> key](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
39+
store.Count | [cardinality(akeys(store))](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
40+
store.Count() | [cardinality(akeys(store))](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Works on `IEnumerable<KeyValuePair<string, string>>` to count concatenated or subtracted stores.
41+
store1 == store2 | [store1 = store2](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
42+
store1.SequenceEqual(store2) | [store1 = store2](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
43+
store.ContainsKey(key) | [store ? key](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
44+
store.Remove(key) | [store - key](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Only `ImmutableDictionary<string, string>`. Compatible with columns mapped as `json` and `jsonb`
45+
store.ContainsValue(value) | [value = ANY(avals(store))](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
46+
store.IsEmpty | [cardinality(akeys(store)) = 0](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Only `ImmutableDictionary<string, string>`
47+
store.Any() | [cardinality(akeys(store)) <> 0](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
48+
store.ToDictionary() | [store](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Converts an `IEnumerable<KeyValuePair<string, string>>` to a `Dictionary<string, string>`. Compatible with columns mapped as `json` and `jsonb`
49+
store.ToImmutableDictionary() | [store](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Converts an `IEnumerable<KeyValuePair<string, string>>` to an `ImmutableDictionary<string, string>`. Compatible with columns mapped as `json` and `jsonb`
50+
store1.Concat(store2) | [store1 \|\| store2](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Call `ToDictionary()` or `ToImmutableDictionary()` on result. Compatible with columns mapped as `json` and `jsonb`
51+
store1.Except(store2) | [store1 - store2](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Call `ToDictionary()` or `ToImmutableDictionary()` on result. Compatible with columns mapped as `json` and `jsonb`
52+
store.Keys | [akeys(store)](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Only `ImmutableDictionary<string, string>`
53+
store.Keys.ToList() | [akeys(store)](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
54+
store.Values | [avals(store)](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Only `ImmutableDictionary<string, string>`
55+
store.Values.ToList() | [avals(store)](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
56+
store1.Keys.Concat(store2.Keys.Concat) | [akeys(store1) \|\| akeys(store2) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
57+
store1.Values.Concat(store2.Values.Concat) | [avals(store1) \|\| avals(store2) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
58+
EF.Functions.ValuesForKeys(store, keys) | [store -> keys](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
59+
EF.Functions.ContainsAllKeys(store, keys) | [store ?& keys](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
60+
EF.Functions.ContainsAnyKeys(store, keys) | [store ?\| keys](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
61+
EF.Functions.Contains(store1, store2) | [store1 @> store2 ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Accepts two `IEnumerable<KeyValuePair<string, string>>`. Compatible with columns mapped as `json` and `jsonb`
62+
EF.Functions.ContainedBy(store1, store2) | [store1 <@ store2 ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Accepts two `IEnumerable<KeyValuePair<string, string>>`. Compatible with columns mapped as `json` and `jsonb`
63+
EF.Functions.Remove(store, key) | [store - key](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
64+
EF.Functions.Slice(store, keys) | [ slice(store, keys) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
65+
EF.Functions.ToKeysAndValues(store) | [hstore_to_array](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) | Compatible with columns mapped as `json` and `jsonb`
66+
EF.Functions.FromKeysAndValues(keysAndValues) | [ hstore(keysAndValues) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
67+
EF.Functions.FromKeysAndValues(keys, values) | [ hstore(keys, values) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
68+
EF.Functions.ToJson(store) | [ hstore_to_json(store) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
69+
EF.Functions.ToJsonb(store) | [ hstore_to_jsonb(store) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
70+
EF.Functions.ToJsonLoose(store) | [ hstore_to_json_loose(store) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
71+
EF.Functions.ToJsonbLoose(store) | [ hstore_to_jsonb_loose(store) ](https://www.postgresql.org/docs/current/hstore.html#HSTORE-OPS-FUNCS) |
72+
EF.Functions.FromJson(json) | [ select hstore(array_agg(key), array_agg(value)) FROM json_each_text(json) ](https://www.postgresql.org/docs/9.3/functions-json.html) | Not natively supported in PostgreSQL
73+
EF.Functions.FromJsonb(json) | [ select hstore(array_agg(key), array_agg(value)) FROM jsonb_each_text(json) ](https://www.postgresql.org/docs/9.3/functions-json.html) | Not natively supported in PostgreSQL
74+
75+

conceptual/EFCore.PG/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
href: mapping/array.md
5353
- name: Enums
5454
href: mapping/enum.md
55+
- name: Hstores
56+
href: mapping/hstore.md
5557
- name: Ranges
5658
href: mapping/range.md
5759
- name: Miscellaneous

0 commit comments

Comments
 (0)