# Integrations

Piggie provides native integrations with LangChain, LlamaIndex, NetworkX, and PyTorch Geometric (PyG).

## LangChain

### PiggieVectorStore

LangChain-compatible VectorStore backed by pgvector. Stores documents with text, metadata, and embedding columns in a SQL table.

```python
from piggie._integrations.langchain import PiggieVectorStore

vs = PiggieVectorStore(
    driver,
    embedding,                # LangChain Embeddings instance
    table="documents",        # table name (default: "langchain_documents")
    create_table=True,        # auto-create table if missing
    embedding_dimensions=768, # vector dimensions (for table creation)
)
```

**Adding documents:**

```python
ids = vs.add_texts(
    ["First document", "Second document"],
    metadatas=[{"source": "a"}, {"source": "b"}],
)
```

**Searching:**

```python
# By text query (embeds automatically)
docs = vs.similarity_search("search query", k=4)

# With scores
docs_with_scores = vs.similarity_search_with_score("search query", k=4)

# By pre-computed vector
docs = vs.similarity_search_by_vector(embedding_vector, k=4)
```

**Deleting:**

```python
vs.delete(ids=["doc-id-1", "doc-id-2"])
```

**Factory method:**

```python
vs = PiggieVectorStore.from_texts(
    texts=["doc1", "doc2"],
    embedding=embedding,
    driver=driver,
    table="my_docs",
)
```

### PiggieGraphStore (LangChain)

LangChain-compatible GraphStore backed by Apache AGE. Use with LangChain's `GraphCypherQAChain` or knowledge graph pipelines.

```python
from piggie._integrations.langchain import PiggieGraphStore

gs = PiggieGraphStore(driver, graph="my_graph")
```

**Adding graph documents:**

```python
gs.add_graph_documents(graph_documents)
```

Accepts LangChain `GraphDocument` objects. Nodes are upserted with MERGE (matched on `id`), relationships are created between matched nodes. All labels are validated against `[A-Za-z_][A-Za-z0-9_]*` and all values use parameterized queries.

**Querying:**

```python
results = gs.query("MATCH (n:Person) RETURN n.name")  # list of dicts
```

**Schema:**

```python
gs.get_schema            # string representation
gs.get_structured_schema # dict with node_props, rel_props, relationships
gs.refresh_schema()      # refresh cached schema from database
```

---

## LlamaIndex

Piggie provides two LlamaIndex integrations: a simple triplet-based `GraphStore` and a full-featured `PropertyGraphStore`.

### PiggieGraphStore (LlamaIndex)

Simple triplet-based store. Stores knowledge as `(:Entity)-[:PREDICATE]->(:Entity)` triples.

```python
from piggie._integrations.llamaindex import PiggieGraphStore

store = PiggieGraphStore(driver, graph="my_graph")
```

**Upserting triplets:**

```python
store.upsert_triplet("Alice", "knows", "Bob")
store.upsert_triplet("Alice", "works_at", "Acme")
```

**Querying:**

```python
# Get triplets for a subject
triplets = store.get("Alice", depth=2, limit=30)
# [["Alice", "knows", "Bob"], ["Alice", "works_at", "Acme"]]

# Get relationship map for multiple subjects
rel_map = store.get_rel_map(subjs=["Alice", "Bob"])
# {"Alice": [["knows", "Bob"], ["works_at", "Acme"]], "Bob": [...]}
```

**Deleting:**

```python
store.delete("Alice", "knows", "Bob")
```

**Schema and raw queries:**

```python
schema_str = store.get_schema(refresh=True)
result_str = store.query("MATCH (n:Entity) RETURN n.name")
```

### PiggiePropertyGraphStore (LlamaIndex)

Modern labeled property graph store. Supports typed nodes, typed relations, structured queries, and vector queries.

Architecture: graph structure lives in AGE (vertices/edges), embeddings live in a companion pgvector table (`{graph}_embeddings`).

```python
from piggie._integrations.llamaindex_property import PiggiePropertyGraphStore

store = PiggiePropertyGraphStore(driver, graph="my_graph")
```

**Upserting nodes:**

```python
from llama_index.core.graph_stores.types import EntityNode, ChunkNode

nodes = [
    EntityNode(id_="alice", label="Person", properties={"age": 30}),
    ChunkNode(id_="doc1", text="Some document text"),
]
store.upsert_nodes(nodes)
```

**Upserting relations:**

```python
from llama_index.core.graph_stores.types import Relation

rels = [
    Relation(source_id="alice", target_id="bob", label="KNOWS"),
]
store.upsert_relations(rels)
```

**Querying:**

```python
# Get nodes by ID
nodes = store.get(ids=["alice"])

# Get nodes by properties
nodes = store.get(properties={"age": 30})

# Get triplets
triplets = store.get_triplets(entity_names=["Alice"])

# Get relationship map
rel_map = store.get_rel_map(subjs=["Alice"], depth=2)

# Structured Cypher query
nodes = store.structured_query(
    "MATCH (n:Person) WHERE n.age > 25 RETURN n",
    param_map={}
)
```

**Vector queries:**

```python
from llama_index.core.vector_stores.types import VectorStoreQuery

vq = VectorStoreQuery(query_embedding=[0.1, 0.2, ...], similarity_top_k=5)
result = store.vector_query(vq)
# Returns (nodes, scores)
```

**Deleting:**

```python
store.delete(ids=["alice"], delete_from_graph=True)
```

**Schema:**

```python
schema = store.get_schema()
```

---

## NetworkX

Convert AGE graphs to NetworkX for analysis and visualization.

### to_networkx

```python
G = db.to_networkx()  # full graph as a DiGraph

# Filter by label/type
G = db.to_networkx(labels=["Person"], rel_types=["KNOWS"])

# Undirected
G = db.to_networkx(directed=False)

# Without properties (faster for large graphs)
G = db.to_networkx(node_properties=False, edge_properties=False)
```

### Graph algorithms

19 algorithms across 7 categories use NetworkX under the hood. See the full [Graph Algorithms](algorithms.md) guide for details.

```python
# Centrality (5 measures)
df = db.centrality(measure="degree")       # also: betweenness, closeness, eigenvector, harmonic

# Community detection (4 methods)
df = db.communities(method="louvain")      # also: label_propagation, greedy_modularity, leiden

# Pathfinding
path = db.shortest_path(source_id, target_id, weight="distance")
paths = db.all_shortest_paths(source_id, target_id)

# PageRank
df = db.pagerank(damping=0.85)

# Similarity
df = db.similarity(measure="jaccard", node_pairs=[(id_a, id_b)])

# Link prediction
df = db.link_prediction(measure="preferential_attachment")

# Embeddings (requires pip install piggie[embeddings])
df = db.node2vec(dimensions=128)
```

---

## PyTorch Geometric (PyG)

Train GNNs on AGE graph data. Piggie provides PyG's `FeatureStore` and `GraphStore` interfaces.

### PiggieFeatureStore

Stores node feature tensors in PostgreSQL (serialized as bytea).

```python
from piggie._integrations.pyg import PiggieFeatureStore
from torch_geometric.data.feature_store import TensorAttr

fs = PiggieFeatureStore(driver, graph="my_graph")

# Store features
fs._put_tensor(tensor, TensorAttr("Person", "x"))

# Retrieve features
t = fs._get_tensor(TensorAttr("Person", "x"))

# List stored attributes
attrs = fs.get_all_tensor_attrs()
```

### PiggieGraphStore (PyG)

Extracts edge topology in COO format from AGE. Node IDs are remapped from AGE's 64-bit IDs to contiguous 0-based indices as required by PyG.

```python
from piggie._integrations.pyg import PiggieGraphStore
from torch_geometric.data.graph_store import EdgeAttr

gs = PiggieGraphStore(driver, graph="my_graph")

# Get edge index [2, num_edges] for a specific edge type
edge_index = gs._get_edge_index(
    EdgeAttr(edge_type=("Person", "KNOWS", "Person"), layout="coo")
)

# List all edge types
edge_attrs = gs.get_all_edge_attrs()
```

### Typical PyG workflow

```python
import piggie
from piggie._integrations.pyg import PiggieFeatureStore, PiggieGraphStore

db = piggie.connect("postgresql://...", graph="social")

# 1. Extract edge topology
gs = PiggieGraphStore(db, graph="social")
edge_index = gs._get_edge_index(
    EdgeAttr(edge_type=("Person", "KNOWS", "Person"), layout="coo")
)

# 2. Load/compute node features
fs = PiggieFeatureStore(db, graph="social")
# ... compute embeddings, store with fs._put_tensor(...)

# 3. Build PyG Data object
from torch_geometric.data import Data
data = Data(x=node_features, edge_index=edge_index)

# 4. Train your GNN
# ...
```
