Skip to content

Overview

Taiyo provides two client implementations for interacting with Apache Solr: a synchronous client (SolrClient) and an asynchronous client (AsyncSolrClient). Both share the same API surface with the only difference being that async methods must be awaited.

Client Setup

from taiyo import SolrClient

with SolrClient("http://localhost:8983/solr") as client:
    client.set_collection("my_collection")
    results = client.search("*:*")
from taiyo import AsyncSolrClient

async with AsyncSolrClient("http://localhost:8983/solr") as client:
    client.set_collection("my_collection")
    results = await client.search("*:*")

Additional Configurations

from taiyo import SolrClient

client = SolrClient(
    base_url="http://localhost:8983/solr",
    timeout=30.0,              # Request timeout in seconds
    verify=True,               # SSL certificate verification
)
from taiyo import AsyncSolrClient

client = AsyncSolrClient(
    base_url="http://localhost:8983/solr",
    timeout=30.0,              # Request timeout in seconds
    verify=True,               # SSL certificate verification
)

SSL Configuration

from taiyo import SolrClient

client = SolrClient(
    "https://solr.example.com/solr",
    verify=False
)

client = SolrClient(
    "https://solr.example.com/solr",
    verify="/path/to/ca-bundle.crt"
)
from taiyo import AsyncSolrClient

client = AsyncSolrClient(
    "https://solr.example.com/solr",
    verify=False
)

client = AsyncSolrClient(
    "https://solr.example.com/solr",
    verify="/path/to/ca-bundle.crt"
)

httpx Options

Pass any httpx client options:

import httpx
from taiyo import SolrClient

client = SolrClient(
    "http://localhost:8983/solr",
    timeout=30.0,
    limits=httpx.Limits(max_connections=100),
    http2=True,
    follow_redirects=True,
)
import httpx
from taiyo import AsyncSolrClient

client = AsyncSolrClient(
    "http://localhost:8983/solr",
    timeout=30.0,
    limits=httpx.AsyncLimits(max_connections=100),
    http2=True,
    follow_redirects=True,
)

Context Managers

Context managers ensure proper resource cleanup:

with SolrClient("http://localhost:8983/solr") as client:
    client.set_collection("my_collection")
    results = client.search("*:*")
async with AsyncSolrClient("http://localhost:8983/solr") as client:
    client.set_collection("my_collection")
    results = await client.search("*:*")

Manual Management

Manual cleanup without context managers:

client = SolrClient("http://localhost:8983/solr")
try:
    results = client.search("*:*")
finally:
    client.close()
from taiyo import AsyncSolrClient


async def main() -> None:
    client = AsyncSolrClient("http://localhost:8983/solr")
    try:
        results = await client.search("*:*")
    finally:
        await client.close()

Setting the Collection

Most operations require an active collection:

from taiyo import SolrClient

client = SolrClient("http://localhost:8983/solr")
client.set_collection("my_collection")

results = client.search("*:*")
client.add(documents)
client.add_field(field)
from taiyo import AsyncSolrClient


async def main() -> None:
    client = AsyncSolrClient("http://localhost:8983/solr")
    client.set_collection("my_collection")

    results = await client.search("*:*")
    await client.add(documents)
    await client.add_field(field)

Switching Collections

You can change the active collection at any time:

client.set_collection("collection1")
results1 = client.search("*:*")

client.set_collection("collection2")
results2 = client.search("*:*")
client.set_collection("collection1")
results1 = await client.search("*:*")

client.set_collection("collection2")
results2 = await client.search("*:*")

Core Operations

Ping

Check if Solr is reachable:

if client.ping():
    print("Solr is available")
if await client.ping():
    print("Solr is available")

Create Collection

Create a new collection:

client.create_collection(
    name="my_collection",
    num_shards=2,
    replication_factor=1,
    maxShardsPerNode=2,
    collection_configName="myconfig",
)
await client.create_collection(
    name="my_collection",
    num_shards=2,
    replication_factor=1,
    maxShardsPerNode=2,
    collection_configName="myconfig",
)

Delete Collection

Delete a collection:

client.delete_collection("my_collection")
await client.delete_collection("my_collection")

Document Operations

Adding Documents

Add a single document:

from taiyo import SolrDocument

doc = SolrDocument(
    title="Hello World",
    content="This is a test document"
)

client.add(doc, commit=True)
from taiyo import AsyncSolrClient, SolrDocument


async def main() -> None:
    client = AsyncSolrClient("http://localhost:8983/solr")
    doc = SolrDocument(
        title="Hello World",
        content="This is a test document"
    )
    await client.add(doc, commit=True)

Add multiple documents:

docs = [
    SolrDocument(title="First"),
    SolrDocument(title="Second"),
    SolrDocument(title="Third")
]

client.add(docs, commit=True)
from taiyo import AsyncSolrClient, SolrDocument


async def main() -> None:
    client = AsyncSolrClient("http://localhost:8983/solr")
    docs = [
        SolrDocument(title="First"),
        SolrDocument(title="Second"),
        SolrDocument(title="Third")
    ]
    await client.add(docs, commit=True)

Committing Changes

Commit pending changes explicitly:

client.add(doc, commit=False)
client.commit()
await client.add(doc, commit=False)
await client.commit()

Deleting Documents

Delete by ID:

client.delete(ids=["1", "2", "3"], commit=True)
await client.delete(ids=["1", "2", "3"], commit=True)

Delete by query:

client.delete(query="status:archived", commit=True)
await client.delete(query="status:archived", commit=True)

Delete all documents:

client.delete(query="*:*", commit=True)
await client.delete(query="*:*", commit=True)

Searching

# Simple query string
results = client.search("title:test")

# With parameters
results = client.search("*:*", rows=20, start=0)
# Simple query string
results = await client.search("title:test")

# With parameters
results = await client.search("*:*", rows=20, start=0)

Using Query Parsers

from taiyo.parsers import StandardParser

parser = StandardParser(
    query="laptop",
    query_operator="AND",
    default_field="content"
)

results = client.search(parser)
from taiyo.parsers import StandardParser

parser = StandardParser(
    query="laptop",
    query_operator="AND",
    default_field="content"
)

results = await client.search(parser)

With Custom Document Models

from taiyo import SolrDocument

class Product(SolrDocument):
    name: str
    price: float
    category: str

results = client.search("*:*", document_model=Product)

for product in results.docs:
    print(f"{product.name}: ${product.price}")
from taiyo import SolrDocument

class Product(SolrDocument):
    name: str
    price: float
    category: str

results = await client.search("*:*", document_model=Product)

for product in results.docs:
    print(f"{product.name}: ${product.price}")

Schema Management

Adding Field Types

from taiyo.schema import SolrFieldType, SolrFieldClass

field_type = SolrFieldType(
    name="text_custom",
    solr_class=SolrFieldClass.TEXT,
    position_increment_gap=100
)

client.add_field_type(field_type)
from taiyo.schema import SolrFieldType, SolrFieldClass

field_type = SolrFieldType(
    name="text_custom",
    solr_class=SolrFieldClass.TEXT,
    position_increment_gap=100
)

await client.add_field_type(field_type)

Adding Fields

from taiyo.schema import SolrField

field = SolrField(
    name="custom_field",
    type="text_general",
    indexed=True,
    stored=True
)

client.add_field(field)
from taiyo.schema import SolrField

field = SolrField(
    name="custom_field",
    type="text_general",
    indexed=True,
    stored=True
)

await client.add_field(field)

Adding Dynamic Fields

from taiyo.schema import SolrDynamicField

dynamic_field = SolrDynamicField(
    name="*_txt",
    type="text_general",
    indexed=True,
    stored=True
)

client.add_dynamic_field(dynamic_field)
from taiyo.schema import SolrDynamicField

dynamic_field = SolrDynamicField(
    name="*_txt",
    type="text_general",
    indexed=True,
    stored=True
)

await client.add_dynamic_field(dynamic_field)

Error Handling

Handle Solr errors gracefully:

from taiyo import SolrError

try:
    results = client.search("invalid:query:[")
except SolrError as e:
    print(f"Error: {e}")
    print(f"Status: {e.status_code}")
    print(f"Response: {e.response}")
from taiyo import SolrError

try:
    results = await client.search("invalid:query:[")
except SolrError as e:
    print(f"Error: {e}")
    print(f"Status: {e.status_code}")
    print(f"Response: {e.response}")

Common error scenarios:

try:
    client.add(doc)
except SolrError as e:
    if e.status_code == 400:
        print("Bad request - check document format")
    elif e.status_code == 404:
        print("Collection not found")
    elif e.status_code == 500:
        print("Solr server error")
try:
    await client.add(doc)
except SolrError as e:
    if e.status_code == 400:
        print("Bad request - check document format")
    elif e.status_code == 404:
        print("Collection not found")
    elif e.status_code == 500:
        print("Solr server error")

Best Practices

Use Context Managers

with SolrClient("http://localhost:8983/solr") as client:
    results = client.search("*:*")
async with AsyncSolrClient("http://localhost:8983/solr") as client:
    results = await client.search("*:*")

Set Collection Once

client = SolrClient("http://localhost:8983/solr")
client.set_collection("my_collection")
client = AsyncSolrClient("http://localhost:8983/solr")
client.set_collection("my_collection")

Batch Operations

docs = [SolrDocument(title=f"Document {i}") for i in range(1000)]
client.add(docs, commit=True)
docs = [SolrDocument(title=f"Document {i}") for i in range(1000)]
await client.add(docs, commit=True)

Avoid individual operations in loops:

for i in range(1000):
    doc = SolrDocument(title=f"Document {i}")
    client.add(doc, commit=True)
for i in range(1000):
    doc = SolrDocument(title=f"Document {i}")
    await client.add(doc, commit=True)

Commit Strategy

For bulk indexing:

for batch in batches:
    client.add(batch, commit=False)
client.commit()
for batch in batches:
    await client.add(batch, commit=False)
await client.commit()

For real-time updates:

client.add(doc, commit=True)
await client.add(doc, commit=True)

Error Handling

try:
    client.create_collection("my_collection")
except SolrError as e:
    if "already exists" in str(e).lower():
        # Collection exists, that's okay
        pass
    else:
        # Unexpected error
        raise
try:
    await client.create_collection("my_collection")
except SolrError as e:
    if "already exists" in str(e).lower():
        # Collection exists, that's okay
        pass
    else:
        # Unexpected error
        raise

Performance Tips

Connection Pooling

httpx automatically handles connection pooling. Configure limits:

client = SolrClient(
    "http://localhost:8983/solr",
    limits=httpx.Limits(
        max_keepalive_connections=10,
        max_connections=50,
        keepalive_expiry=30.0
    )
)
client = AsyncSolrClient(
    "http://localhost:8983/solr",
    limits=httpx.Limits(
        max_keepalive_connections=10,
        max_connections=50,
        keepalive_expiry=30.0
    )
)

Timeout Configuration

Configure timeouts based on operation type:

client = SolrClient("http://localhost:8983/solr", timeout=5.0)

client = SolrClient("http://localhost:8983/solr", timeout=300.0)

client = SolrClient("http://localhost:8983/solr", timeout=httpx.Timeout(
    connect=5.0,
    read=60.0,
    write=30.0,
    pool=10.0
))
client = AsyncSolrClient("http://localhost:8983/solr", timeout=5.0)

client = AsyncSolrClient("http://localhost:8983/solr", timeout=300.0)

client = AsyncSolrClient("http://localhost:8983/solr", timeout=httpx.Timeout(
    connect=5.0,
    read=60.0,
    write=30.0,
    pool=10.0
))

HTTP/2 Support

Enable HTTP/2 for better performance:

client = SolrClient(
    "http://localhost:8983/solr",
    http2=True
)
client = AsyncSolrClient(
    "http://localhost:8983/solr",
    http2=True
)

Next Steps