0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Build a Document Search App in About 10 Lines of Python

0
Posted at

Build a Document Search App in About 10 Lines of Python

Sometimes you just want to search your own documents from Python.

For example:

  • Search notes or text files by keyword
  • Search JSON data
  • Filter documents by category
  • Combine keyword search with metadata
  • Add search to a small local application

You could set up a dedicated search server, but that may be more infrastructure than you need.

With nlp4j-local-search, you can create a small local document search engine directly from Python.

Let's start with the code.

Installation

pip install nlp4j-local-search==0.7.4.0

Latest package:

https://pypi.org/project/nlp4j-local-search/0.7.4.0/

A Document Search App in About 10 Lines

from nlp4j_local_search import SearchEngine

with SearchEngine("en") as engine:
    engine.add("1", "Kyoto is a historic city in Japan.", fields={"category": "city"})
    engine.add("2", "Nintendo is headquartered in Kyoto.", fields={"category": "company"})
    engine.add("3", "Tokyo is the capital city of Japan.", fields={"category": "city"})
    engine.commit()

    for r in engine.search("Kyoto"):
        print(r.id, r.body)

That's basically it.

The result will look like:

1 Kyoto is a historic city in Japan.
2 Nintendo is headquartered in Kyoto.

No separate search server is required.

How It Works

The basic workflow is very small:

add documents
    ↓
commit
    ↓
search

First, create a search engine.

with SearchEngine("en") as engine:

Then add some documents.

engine.add("1", "Kyoto is a historic city in Japan.")

The first argument is the document ID.

The second argument is the text to search.

After adding documents, commit them to the search index.

engine.commit()

Then search.

engine.search("Kyoto")

For a simple document search application, this is almost everything you need.

Add Metadata to Documents

Real-world documents usually have more than just text.

For example, a document may have:

text
category
country
author
product
year

You can attach additional fields when adding a document.

engine.add(
    "1",
    "Kyoto is a historic city in Japan.",
    fields={"category": "city"},
)

Now the document has both text and metadata.

Conceptually:

id       = 1
text     = Kyoto is a historic city in Japan.
category = city

Search by Category

You can search metadata fields too.

engine.search("category:city")

This finds documents whose category is city.

For example:

for r in engine.search("category:city"):
    print(r.body)

could return:

Kyoto is a historic city in Japan.
Tokyo is the capital city of Japan.

Combine Keyword Search and Metadata

This becomes more useful when you combine text search with metadata.

engine.search("Kyoto AND category:company")

This means:

contains "Kyoto"
AND
category is "company"

In our example, the result is:

Nintendo is headquartered in Kyoto.

The Python code is simply:

for r in engine.search("Kyoto AND category:company"):
    print(r.body)

OR Search

You can also search for multiple alternatives.

engine.search("Kyoto OR Tokyo")

This finds documents containing either Kyoto or Tokyo.

for r in engine.search("Kyoto OR Tokyo"):
    print(r.body)

JSON Documents

You can also add documents as dictionaries.

engine.add_json({
    "id": "1",
    "body": "Kyoto is a historic city in Japan.",
    "category": "city",
    "country": "Japan",
})

This is convenient when your source data already comes from JSON or JSONL files.

You can then search fields directly.

engine.search("category:city")

or combine multiple conditions:

engine.search("category:city AND country:Japan")

That makes it possible to build a small structured document search application without introducing a database-style query layer.

Search Results Include Scores

Search results also include a relevance score.

for r in engine.search("Kyoto"):
    print(r.id, r.body, r.score)

A result may look like:

1 Kyoto is a historic city in Japan. 0.18...
2 Nintendo is headquartered in Kyoto. 0.16...

The score can be useful when you want to rank search results by relevance.

More Search Expressions

Once you need more advanced queries, you can extend the search expression.

AND:

engine.search("Kyoto AND historic")

OR:

engine.search("Kyoto OR Tokyo")

NOT:

engine.search("category:city AND NOT country:Japan")

Field search:

engine.search("category:company")

Keyword + field:

engine.search("Kyoto AND category:company")

Wildcard:

engine.search("Kyo*")

Phrase search:

engine.search('"historic city"')

You can start with:

search("Kyoto")

and learn the more advanced forms only when you actually need them.

What's Running Under the Hood?

So far, we have not needed to know anything about the search engine implementation.

Internally, nlp4j-local-search uses Apache Lucene.

Lucene is a mature full-text search library used as the foundation of many search systems.

The architecture is roughly:

Python application
       ↓
nlp4j-local-search
       ↓
Apache Lucene

The idea is that Python users do not need to work directly with Lucene's Java APIs just to add local search to a program.

You can start from a Python API such as:

engine.add(...)
engine.commit()
engine.search(...)

and use the underlying search engine without thinking much about the Java layer.

Why Not Just Use Elasticsearch or OpenSearch?

Elasticsearch, OpenSearch, and Solr are excellent choices when you need a full search server.

For example:

  • Distributed search
  • Multiple application servers
  • Large-scale production deployments
  • REST APIs shared by many clients
  • Cluster management

But sometimes the requirement is much smaller.

You may simply want to:

  • Search a local JSONL dataset
  • Explore documents in Jupyter
  • Build a prototype
  • Add search to a command-line tool
  • Search a few thousand or a few hundred thousand documents
  • Prepare retrieval for an NLP or RAG experiment

In those cases, setting up a separate search server may feel unnecessary.

nlp4j-local-search runs the search engine inside your Python application.

Vector Search Is Also Available

The same package can also perform vector search.

For example:

with SearchEngine("en", vector_dimension=2) as engine:
    engine.add("1", [1.0, 0.0])
    engine.add("2", [0.0, 1.0])
    engine.commit()

    results = engine.search_vector([0.9, 0.1])

So the same local search engine can support:

keyword search
+
field filtering
+
vector search

You do not need vector search to get started, though.

For many applications, plain text search is still the simplest and most useful first step.

From Search to Data Exploration

You can also inspect the distribution of field values.

For example:

print(engine.view("category"))

This can show which categories appear most often in your documents.

That means the package can be used not only for retrieving individual documents, but also for exploring the contents of a dataset.

Summary

A basic local document search application can be surprisingly small.

from nlp4j_local_search import SearchEngine

with SearchEngine("en") as engine:
    engine.add("1", "Kyoto is a historic city in Japan.")
    engine.add("2", "Nintendo is headquartered in Kyoto.")
    engine.commit()

    for r in engine.search("Kyoto"):
        print(r.body)

Start with:

search("Kyoto")

Then, when necessary, add metadata:

search("category:city")

Combine text and metadata:

search("Kyoto AND category:company")

And later, if your application needs it, move on to aggregation or vector search.

The goal of nlp4j-local-search is simple:

make local document search feel like a normal Python operation.

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?