Skip to main content

A Solr client library written in Rust

Reason this release was yanked:

Missing wheels

Project description

Solrstice: A Solr 8+ Client for Rust and Python

Solrstice is a solr client library written in rust. With this wrapper you can use it in python.

Both asyncio and blocking clients are provided. All apis have type hints.

Features

  • Config API
  • Collection API
  • Alias API
  • Select Documents
    • Grouping Component Query
    • Stats Component
    • DefTypes (lucene, dismax, edismax)
    • Facet Counts (Query, Field, Pivot)
    • Json Facet (Query, Stat, Terms, Nested)
  • Indexing Documents
  • Deleting Documents

Installation

pip install solrstice

Basic Usage

Async

import asyncio

from solrstice import SolrBasicAuth, SolrServerContext, SolrSingleServerHost, AsyncSolrCloudClient, UpdateQuery, \
    SelectQuery, DeleteQuery

# A SolrServerContext specifies how the library should interact with Solr
context = SolrServerContext(SolrSingleServerHost('localhost:8983'), SolrBasicAuth('solr', 'SolrRocks'))
client = AsyncSolrCloudClient(context)


async def main() -> None:
    # Create config and collection
    await client.upload_config('example_config', 'path/to/config')
    await client.create_collection('example_collection', 'example_config', shards=1, replication_factor=1)

    # Index a document
    await client.index(UpdateQuery(), 'example_collection', [{'id': 'example_document', 'title': 'Example document'}])

    # Search for the document
    response = await client.select(SelectQuery(fq=['title:Example document']), 'example_collection')
    docs_response = response.get_docs_response()
    assert docs_response is not None
    assert docs_response.get_num_found() == 1
    docs = docs_response.get_docs()

    # Delete the document
    await client.delete(DeleteQuery(ids=['example_document']), 'example_collection')


asyncio.run(main())

Blocking

from solrstice import SolrBasicAuth, BlockingSolrCloudClient, SolrServerContext, SolrSingleServerHost, DeleteQuery, \
    SelectQuery, UpdateQuery

# A SolrServerContext specifies how the library should interact with Solr
context = SolrServerContext(SolrSingleServerHost('localhost:8983'), SolrBasicAuth('solr', 'SolrRocks'))
client = BlockingSolrCloudClient(context)

# Create config and collection
client.upload_config('example_config', 'path/to/config')
client.create_collection('example_collection', 'example_config', shards=1, replication_factor=1)

# Index a document
client.index(UpdateQuery(), 'example_collection', [{'id': 'example_document', 'title': 'Example document'}])

# Search for the document
response = client.select(SelectQuery(fq=['title:Example document']), 'example_collection')
docs_response = response.get_docs_response()
assert docs_response is not None
assert docs_response.get_num_found() == 1
docs = docs_response.get_docs()

# Delete the document
client.delete(DeleteQuery(ids=['example_document']), 'example_collection')

Grouping component

Field grouping

from solrstice import GroupingComponent, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  group_builder = GroupingComponent(fields=["age"], limit=10)
  select_builder = SelectQuery(fq=["age:[* TO *]"], grouping=group_builder)
  groups = (await client.select(select_builder, "example_collection")).get_groups()
  age_group = groups["age"]
  docs = age_group.get_field_result()

Query grouping

from solrstice import GroupingComponent, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  group_builder = GroupingComponent(queries=["age:[0 TO 59]", "age:[60 TO *]"], limit=10)
  select_builder = SelectQuery(fq=["age:[* TO *]"], grouping=group_builder)
  groups = (await client.select(select_builder, "example_collection")).get_groups()
  age_group = groups["age:[0 TO 59]"]
  group = age_group.get_query_result()
  assert group is not None
  docs = group.get_docs()

Query parsers

Lucene

from solrstice import LuceneQuery, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  query_parser = LuceneQuery(df="population")
  select_builder = SelectQuery(q="outdoors", def_type=query_parser)
  response = (await client.select(select_builder, "example_collection")).get_docs_response()
  assert response is not None
  docs = response.get_docs()

Dismax

from solrstice import DismaxQuery, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  query_parser = DismaxQuery(qf="interests^20", bq=["interests:cars^20"])
  select_builder = SelectQuery(q="outdoors", def_type=query_parser)
  response = (await client.select(select_builder, "example_collection")).get_docs_response()
  assert response is not None
  docs = response.get_docs()

Edismax

from solrstice import EdismaxQuery, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  query_parser = EdismaxQuery(qf="interests^20", bq=["interests:cars^20"])
  select_builder = SelectQuery(q="outdoors", def_type=query_parser)
  response = (await client.select(select_builder, "example_collection")).get_docs_response()
  assert response is not None
  docs = response.get_docs()

FacetSet Component

Pivot facet

from solrstice import FacetSetComponent, PivotFacetComponent, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(facet_set=FacetSetComponent(pivots=PivotFacetComponent(["interests,age"])))
  response = await client.select(select_builder, "example_collection")
  facets = response.get_facet_set()
  pivots = facets.get_pivots()
  interests_age = pivots.get("interests,age")

Field facet

from solrstice import FacetSetComponent, FieldFacetComponent, FieldFacetEntry, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  facet_set = FacetSetComponent(fields=FieldFacetComponent(fields=[FieldFacetEntry("age")]))
  select_builder = SelectQuery(facet_set=facet_set)
  response = await client.select(select_builder, "example_collection")
  facets = response.get_facet_set()
  fields = facets.get_fields()
  age = fields.get("age")

Query facet

from solrstice import AsyncSolrCloudClient, SolrServerContext, SelectQuery, FacetSetComponent, FacetSetComponent
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(facet_set=FacetSetComponent(queries=["age:[0 TO 59]"]))
  response = await client.select(select_builder, "example_collection")
  facets = response.get_facet_set()
  queries = facets.get_queries()
  query = queries.get("age:[0 TO 59]")

Stats Component

from solrstice import StatsComponent, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(stats=StatsComponent(fields=["age"]))
  response = await client.select(select_builder, "example_collection")
  stats = response.get_stats()
  assert stats is not None
  age_stats = stats.get_fields()["age"]
  age_count = age_stats.get_count()

Json Facet Component

Query

from solrstice import JsonFacetComponent, JsonQueryFacet, SelectQuery, SolrServerContext, AsyncSolrCloudClient
async def main() -> None:
  client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))
  select_builder = SelectQuery(
      json_facet=JsonFacetComponent(
          facets={"below_60": JsonQueryFacet(q="age:[0 TO 59]")}
      )
  )
  response = await client.select(select_builder, "example_collection")
  facets = response.get_json_facets()
  assert facets is not None
  below_60 = facets.get_nested_facets().get("below_60")
  assert below_60 is not None
  assert below_60.get_count() == 4

Stat

from solrstice import JsonFacetComponent, JsonStatFacet, SelectQuery, SolrServerContext, AsyncSolrCloudClient
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(
      json_facet=JsonFacetComponent(
          facets={"total_people": JsonStatFacet("sum(count)")}
      )
  )
  response = await client.select(select_builder, "example_collection")
  facets = response.get_json_facets()
  assert facets is not None
  total_people = facets.get_flat_facets()["total_people"]
  assert total_people == 1000

Terms

from solrstice import AsyncSolrCloudClient, SolrServerContext, SelectQuery, JsonFacetComponent, JsonTermsFacet
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(
      json_facet=JsonFacetComponent(facets={"age": JsonTermsFacet("age")})
  )
  response = await client.select(select_builder, "example_collection")
  facets = response.get_json_facets()
  assert facets is not None
  age_buckets = facets.get_nested_facets()["age"].get_buckets()
  assert len(age_buckets) == 3

Nested

from solrstice import AsyncSolrCloudClient, SolrServerContext, SelectQuery, JsonFacetComponent, JsonQueryFacet, JsonStatFacet
client = AsyncSolrCloudClient(SolrServerContext('localhost:8983'))

async def main() -> None:
  select_builder = SelectQuery(
      json_facet=JsonFacetComponent(
          facets={
              "below_60": JsonQueryFacet(
                  q="age:[0 TO 59]",
                  facets={"total_people": JsonStatFacet("sum(count)")},
              )
          }
      )
  )
  response = await client.select(select_builder, "example_collection")
  facets = response.get_json_facets()
  assert facets is not None
  total_people = (
      facets.get_nested_facets()
      ["below_60"]
      .get_flat_facets()
      .get("total_people")
  )
  assert total_people == 750.0

Hosts

Single Server

from solrstice import SolrServerContext, SolrSingleServerHost, SolrBasicAuth, AsyncSolrCloudClient

context = SolrServerContext(SolrSingleServerHost('localhost:8983'), SolrBasicAuth('solr', 'SolrRocks'))
client = AsyncSolrCloudClient(context)

Multiple servers

from solrstice import SolrServerContext, SolrMultipleServerHost, SolrBasicAuth, AsyncSolrCloudClient

# The client will randomly select a server to send requests to. It will wait 5 seconds for a response, before trying another server.
context = SolrServerContext(
    SolrMultipleServerHost(["localhost:8983", "localhost:8984"], 5),
    SolrBasicAuth('solr', 'SolrRocks'),
)
client = AsyncSolrCloudClient(context)

Zookeeper

from solrstice import SolrServerContext, ZookeeperEnsembleHostConnector, SolrBasicAuth, AsyncSolrCloudClient

async def main() -> None:
  context = SolrServerContext(
      await ZookeeperEnsembleHostConnector(["localhost:2181"], 30).connect(),
      SolrBasicAuth('solr', 'SolrRocks'),
  )
  client = AsyncSolrCloudClient(context)

Notes

  • Multiprocessing does not work, and will block forever. Normal multithreading works fine.

  • Pyo3, the Rust library for creating bindings does not allow overriding the __init__ method on objects from Rust. __new__ has to be overridden instead.

    For example, if you want to create a simpler way to create a client

    from typing import Optional
    from solrstice import SolrServerContext, SolrSingleServerHost, SolrBasicAuth, AsyncSolrCloudClient, SolrAuth
    class SolrClient(AsyncSolrCloudClient):
        def __new__(cls, host: str, auth: Optional[SolrAuth] = None):
            context = SolrServerContext(SolrSingleServerHost(host), auth)
            return super().__new__(cls, context=context)
    client = SolrClient("localhost:8983", SolrBasicAuth("username", "password"))
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

solrstice-0.15.0.tar.gz (106.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

solrstice-0.15.0-cp39-abi3-musllinux_1_2_x86_64.whl (5.0 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ x86-64

solrstice-0.15.0-cp39-abi3-manylinux_2_28_s390x.whl (4.4 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ s390x

File details

Details for the file solrstice-0.15.0.tar.gz.

File metadata

  • Download URL: solrstice-0.15.0.tar.gz
  • Upload date:
  • Size: 106.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"25.11","id":"xantusia","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for solrstice-0.15.0.tar.gz
Algorithm Hash digest
SHA256 715f057559c009445b473bf76f37531f4de7366223a6f1fed8e98eebd8815e00
MD5 87755ba867b9d65fb783a7eca5c26cf1
BLAKE2b-256 78989e3d34f1eab859855a4b5f46837c127071e35632c7a854cc65f6d7bc5935

See more details on using hashes here.

File details

Details for the file solrstice-0.15.0-cp39-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: solrstice-0.15.0-cp39-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 5.0 MB
  • Tags: CPython 3.9+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for solrstice-0.15.0-cp39-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ad7789c673b951772238594d8c1e690798fa1260abfa1383ea19c8a2e8d1f5f9
MD5 ddd7100ce1be432f26c8592366727dae
BLAKE2b-256 5e3823234ee7873d01021bc069e6c7f155329a708280a87e023c882c067714bb

See more details on using hashes here.

File details

Details for the file solrstice-0.15.0-cp39-abi3-manylinux_2_28_s390x.whl.

File metadata

  • Download URL: solrstice-0.15.0-cp39-abi3-manylinux_2_28_s390x.whl
  • Upload date:
  • Size: 4.4 MB
  • Tags: CPython 3.9+, manylinux: glibc 2.28+ s390x
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for solrstice-0.15.0-cp39-abi3-manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 289fb09d0511745b32d5badf1bbcea646248142c5ef0f8305467ef117dec844d
MD5 a4949a10b59531374c99dba01b59d40e
BLAKE2b-256 60710eef60072bb66e6996d291639ddcd96597d0cff45d0e4a4399bcad3f7084

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page