Connect profiles and search bios
The language tabs use earlier client interfaces. Check SDK versions and connection setup before running them.
Store a profile with its follows relationship, then index the bio by meaning. The relationship comes from the schema; the embedding is a separate write.
Before you run it: Following or unfollowing replaces the full stored relationship list in this example. Do not submit a partial delta as the full list.
Use the tenant-scoped HTTP connection described in the connection notes. In these snippets, ORIGINCHAIN_URL is the origin including https://, T is its tenant ID, and OC_TOKEN is its bearer token. Do not append these routes to a different Connect URL format.
- Save Alice's profileInclude the complete list of people Alice follows.
- Index the biographyStore its embedding under Alice's profile ID.
- Search or follow linksFind similar profiles or traverse the declared relationships.
- Read the profileFetch the current user fields using the returned ID.
Follow the example
- Create the schema and related rows.
- Write the profile and follows values.
- Write the bio embedding with the same ID.
- Read relationships or query similar bios.
Create the profile table
Push this once. The follows list column plus the self-relation is the whole graph - no separate edge table to keep in sync.
# social/users.toml - one table models the profile AND the graph.
namespace = "social"
table = "users"
primary_key = ["id"]
[[columns]]
name = "id"
ty = "str"
required = true
[[columns]]
name = "handle"
ty = "str"
required = true
[[columns]]
name = "bio"
ty = "str"
[[columns]]
name = "joined_ms"
ty = "u64"
# The follow graph lives in a list column on the row. Each element is a
# user id this person follows.
[[columns]]
name = "follows"
ty = "list"
element = "str"
[[indexes]]
name = "by_handle"
columns = ["handle"]
# A self-relation turns the 'follows' list into graph edges: one forward
# edge per element (alice -> bob, alice -> carol). bidirectional = true also
# maintains the reverse side, so "who follows bob?" is a graph read, not a scan.
[[relations]]
name = "following"
from_col = "follows"
bidirectional = true
[relations.target]
namespace = "social"
table = "users"
pk = "id"The user row (profile + follows)
The follows array lands as graph edges in the same write. To follow or unfollow later, re-put the row with the updated list - the edges are recomputed from the new list on each write.
curl -X POST "$ORIGINCHAIN_URL/v1/tenants/$T/rows/social.users" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "alice",
"handle": "@alice",
"bio": "Mechanical engineer. Trail runner. Reading sci-fi.",
"joined_ms": 1746180851000,
"follows": ["bob", "carol"]
}'The profile embedding
Use the same id as the user row. Now /vector/social.users/topk returns similar users by bio.
curl -X POST "$ORIGINCHAIN_URL/v1/tenants/$T/vector/social.users/put" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "alice",
"embedding": [0.0182, -0.0712, 0.0419, /* ... 768 floats ... */],
"dim": 768,
"metric": "cosine"
}'Traverse the follow graph
neighbors walks one hop forward along the relation (who alice follows); reverse walks the back-edge (who follows bob). Both are direct adjacency-list lookups - no scan, no join.
# Who does alice follow? (one hop forward along 'following')
curl "$ORIGINCHAIN_URL/v1/tenants/$T/graph/social.users/neighbors?rel=following&pk=alice" \
-H "Authorization: Bearer $OC_TOKEN"
# → ["bob", "carol"]
# Who follows bob? (reverse side, maintained by bidirectional = true)
curl "$ORIGINCHAIN_URL/v1/tenants/$T/graph/social.users/reverse?rel=following&pk=bob" \
-H "Authorization: Bearer $OC_TOKEN"
# → ["alice"]The embedding arrays are shortened illustrations. Generate the full vector with your chosen model, match dim to its length, and use that same model when embedding a search question. An embedding is a numeric representation used to compare meaning; OriginChainDB does not generate it in these write calls.
Handle separate writes and retries
These write calls commit separately. The documented SDKs do not provide automatic retry safety. A new SDK call can generate a new key. Retry reads with bounded backoff; for writes, confirm the endpoint supports deduplication and reuse a stable key, or reconcile the previous outcome first. See idempotency.
Important limits and checks
- Modeling follows as a composite-PK edge table. A
followstable keyed[follower_id, followee_id]can't be traversed withneighbors(pk="alice")- the source of each edge is the whole composite key, not a single user. Put the relation on theusersrow via afollowslist instead. - Forgetting the reverse direction. Set
bidirectional = trueso "who follows bob?" is areverseread. Without it you can only walk forward. - Partial re-put on follow/unfollow. Edges are recomputed from the
followslist on each write, so re-put the row with the full new list, not just the delta. - Updating handle without re-embedding. If you embed the handle and let users edit it, the vector goes stale until you re-put. Either re-put on every profile edit or only embed the bio.
Expected result
The forward relationship from alice points to bob and carol; a reverse read for bob should include alice. Those keys locate profiles, not profile bodies. Create Bob and Carol's rows if the application must display them, and use the vector result IDs to read bios after a similarity search.