redis-om-python icon indicating copy to clipboard operation
redis-om-python copied to clipboard

Unable to index a `HashModel` field in a `JsonModel`.

Open wiseaidev opened this issue 1 year ago • 2 comments

Storing works, but querying doesn't work. Take the following as an example:

from typing import Optional, Any

from fastapi import FastAPI

from pydantic import BaseModel, Field as PydanticField, EmailStr
import datetime

from aredis_om import (
    Field,
    HashModel,
    JsonModel,
    Migrator,
    get_redis_connection
)

redis_conn = get_redis_connection(
    url=f"redis://localhost:6379",
    decode_responses=True
)

class User(HashModel):
    first_name: Optional[str] = Field(index=True)
    last_name: Optional[str] = Field(index=True)
    email: EmailStr = Field(index=True)
    password: str = Field(index=True)
    created_on: Optional[datetime.datetime] = Field(default_factory=datetime.datetime.now)

    class Meta:
        database = redis_conn

class Contact(JsonModel):
    user: User = Field(index=True)
    contact: User = Field(index=True)
    favourite: Optional[str] = Field(index=True, default=False)
    created_on: Optional[datetime.datetime] = Field(default_factory=datetime.datetime.now)
    class Meta:
        database = redis_conn

router = FastAPI(title=__name__)

@router.on_event("startup")
async def startup():
    await Migrator().run()
    user1 = await User(email="[email protected]", password="S3C11R3P@ssW0rD").save()
    user2 = await User(email="[email protected]", password="P@ssW0rD").save()
    contact = await Contact(user=user1, contact=user2).save()
    contact = await Contact.find(Contact.user == user1).all() # <------------------- This query will fail

Running the above code uvicorn your_file_name:router --reload will generate the following exception:

    return self.escaped_chars_re.sub(escape_symbol, value)
TypeError: expected string or bytes-like object

Is there any way to index such a field?

Edit

Another question related to a field that contains a list of HashModel instances like the following:

class Contact(JsonModel):
    user: User = Field(index=True)
    contacts: list[User] = Field(index=True)
    favourite: Optional[str] = Field(index=True, default=False)
    created_on: Optional[datetime.datetime] = Field(default_factory=datetime.datetime.now)
    class Meta:
        database = redis_conn

Quoting from the docs:

Redis Hashes can’t store nested containers like Lists, Sets, or other Hashes, so this doesn’t work.

So, it seems like it is not possible to store lists of HashModel. However, i built some custom logic to dynamically build a list from all the records. Another workaround would be to store the emails of users in the contact rather than a user reference:

class Contact(JsonModel):
    user: EmailStr = Field(index=True)
    contacts: list[EmailStr] = Field(index=True)
    favourite: Optional[str] = Field(index=True, default=False)
    created_on: Optional[datetime.datetime] = Field(default_factory=datetime.datetime.now)
    class Meta:
        database = redis_conn

I think this would work, however it stores redundant information. The efficient way, i believe, is to store a reference for each user, a HashModel instance.

wiseaidev avatar Aug 17 '22 17:08 wiseaidev