> Full SurrealDB documentation index: https://surrealdb.com/docs/llms.txt

# SurrealSet

SurrealSet in the Python SDK: the SurrealDB set type as a list subclass, Python set and frozenset sent as sets, server order and de-duplication, equality with lists, keeping a set field a set when a record is written back, SurrealDB 2.x, and pydantic models.

A SurrealDB `set` holds each value once, in an order that the server chooses, and it can hold any value, including objects and arrays. The Python SDK reads every set as a `SurrealSet`, a `list` subclass that the SDK sends back as a set. This page documents how sets convert, and what to check when you write back a record that holds a set. The `set` type needs SurrealDB 3.0 or later.

```python title="Import"
from surrealdb import SurrealSet
```

**Source:** [set.py](https://github.com/surrealdb/surrealdb.py/blob/main/src/surrealdb/data/types/set.py)

---

## How sets convert {#conversion}

| Python | SurrealDB | Read back as |
|---|---|---|
| `set`, `frozenset` | `set` | `SurrealSet` |
| `SurrealSet` | `set` | `SurrealSet` |
| `list`, `tuple` | `array` | `list` |

A Python `set` can only hold hashable values. To send a set that holds objects or arrays, use a `SurrealSet`.

---

## Constructor {#constructor}

```python title="Syntax"
SurrealSet(iterable=())
```

<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>iterable</code> _(optional)_</td>
            <td><code>Iterable</code></td>
            <td>The members, as for <code>list()</code>. Defaults to an empty set.</td>
        </tr>
    </tbody>
</table>

A `SurrealSet` that you build does not remove duplicates or sort its members. The server does that when it receives the set, and a set always reads back in the server's order.

```python
from surrealdb import Surreal, SurrealSet

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    tags = SurrealSet(["news", "intro", "news"])
    print(db.query("RETURN $tags", {"tags": tags}).first())
    print(db.query("RETURN $tags", {"tags": {3, 1, 2}}).first())
```

```python title="Output"
SurrealSet(['intro', 'news'])
SurrealSet([1, 2, 3])
```

---

## Working with a `SurrealSet` {#usage}

A `SurrealSet` is a `list`, so you can index it, iterate over it, take its `len()` and test membership with `in`. It is equal to a `list` that holds the same members in the same order, and, as with any list, the order counts.

```python
tags = SurrealSet(["intro", "news"])
print(tags == ["intro", "news"], tags == ["news", "intro"])
print(tags[0], len(tags), "news" in tags)
```

```text title="Output"
True False
intro 2 True
```

---

## Writing a set back {#read-modify-write}

A set field reads as a `SurrealSet`, so a record that you read and write back keeps the field as a set. A `list` is sent as an array. On a schemaless table, writing a list over a set field turns the field into an array, which keeps duplicates from then on. A schemafull field of type `set<T>` converts the array back to a set.

```python
from surrealdb import RecordID, Surreal

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    post = RecordID("post", 1)
    db.create(post, {"tags": {"news", "intro"}})

    sql = "RETURN type::of($post.tags)"

    record = db.select(post).execute()
    db.update(post, record)
    print(db.query(sql, {"post": post}).first())

    db.update(post, {"tags": ["news", "intro"]})
    print(db.query(sql, {"post": post}).first())
```

```text title="Output"
set
array
```

---

## SurrealDB 2.x {#surrealdb-2}

SurrealDB 2.x has no `set` type. It refuses a `set`, `frozenset` or `SurrealSet` with an error that asks for a list, and its `<set>` cast gives an array, which reads back as a `list`. Send a `list` to a SurrealDB 2.x server.

---

## Pydantic models {#pydantic}

A pydantic field typed as `list[...]` turns a `SurrealSet` into a plain `list`. A field typed as `SurrealSet` keeps it, with `arbitrary_types_allowed=True` on the model, but `model_dump()` still gives a plain `list`. Before you write such data back, wrap the value again, for example `data["tags"] = SurrealSet(data["tags"])`.

---

## See also

- [Python values](/docs/reference/python/api/values.md) for every value class
- [Value types](/docs/reference/python/concepts/value-types.md) for how every value converts
- [Sets](/docs/reference/query-language/language-primitives/data-types/sets.md) in SurrealQL
