Skip to content

RI-7190 Field Box component #4716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: feature/RI-6855/vector-search
Choose a base branch
from

Conversation

valkirilov
Copy link
Collaborator

Description

Create base components for the "field box" and a "group of field boxes" that can be used in the "Create Index" step part of the new Vector Search feature

  • integrate the MultiBoxSelectionGroup component from Redis UI
  • created two standalone components, FieldBox and FieldBoxesGroup, that can be used together to present the interface for picking which fields to be added to the new Vector Search index
image

How to test it

These are base components that will be included in the new wizard for the Vector Search, but currently, you can't find them integrated anywhere in the flow so far. In the first version of the feature, they should look similar to the following preview

image

How to use it

FieldBox component

// Example field details
const box: VectorSearchBox = {
    value: 'id',
    label: 'id',
    text: 'Unique product identifier',
    tag: FieldTypes.TAG,
    disabled: true,
};

// And here is how we init the component itself, always wrapped inside a group parent
<BoxSelectionGroup.Compose>
    <FieldBox box={box} />
</BoxSelectionGroup.Compose>

FieldBoxesGroup component

// Example data fields
const boxes: VectorSearchBox[] = [
  {
    value: 'id',
    label: 'id',
    text: 'Unique product identifier',
    tag: FieldTypes.TAG,
    disabled: true,
  },
  // and more fields here...
]

// Helper state variables and modifiers
const [selectedBoxes, setSelectedBoxes] = useState<string[]>(
    getDefaultSelectedBoxes(boxes),
  )

const handleBoxSelectionChange = (newValue: string[] | undefined) => {
    setSelectedBoxes(newValue || [])
}

// And here is how we init the component itself
<FieldBoxesGroup
   boxes={boxes}
   value={selectedBoxes}
   onChange={handleBoxSelectionChange}
/>

Full Example

  • wrapper component
const ExampleWrapper = () => {
  const { boxes } = useIndexFields(BIKES_INDEX_FIELDS)

  const [selectedBoxes, setSelectedBoxes] = useState<string[]>(
    getDefaultSelectedBoxes(boxes),
  )

  const handleBoxSelectionChange = (newValue: string[] | undefined) => {
    setSelectedBoxes(newValue || [])
  }

  return (
      <FieldBoxesGroup
         boxes={boxes}
         value={selectedBoxes}
         onChange={handleBoxSelectionChange}
      />
  )
}
  • preset with example data
export interface RedisIndexField {
  name: string
  type: FieldTypes
  description?: string
}

// Note: We start with this preset of hardcoded fields for the Bikes index, but in the future, we might want to fetch these fields from a server or allow users to customize them.
export const BIKES_INDEX_FIELDS: RedisIndexField[] = [
  {
    name: 'id',
    type: FieldTypes.TAG,
    description: 'Unique product identifier',
  },
  {
    name: 'description',
    type: FieldTypes.TEXT,
    description: 'Product description',
  },
  {
    name: 'price',
    type: FieldTypes.NUMERIC,
    description: 'Product price',
  },
  {
    name: 'price_1',
    type: FieldTypes.NUMERIC,
    description: 'Product price',
  },
  {
    name: 'name',
    type: FieldTypes.TEXT,
    description: 'Product name',
  },
  { name: 'category', type: FieldTypes.TAG, description: 'Product category' },
  {
    name: 'embedding',
    type: FieldTypes.VECTOR,
    description: 'Product embedding vector',
  },
  {
    name: 'embedding_1',
    type: FieldTypes.VECTOR,
    description: 'Product embedding vector',
  },
]
  • helper hooks and utils to keep the state somehow isolated from the main component
interface UseIndexFieldsHook {
  boxes: VectorSearchBox[]
}

export const useIndexFields = (
  indexFields: RedisIndexField[],
): UseIndexFieldsHook => {
  const boxes = indexFields.map(convertIndexFieldToBoxSelectionGroupBox)

  return {
    boxes,
  }
}

const convertIndexFieldToBoxSelectionGroupBox = (
  field: RedisIndexField,
  index: number,
): VectorSearchBox => ({
  value: field.name,
  label: field.name,
  text: field?.description ?? '',
  tag: field.type,
  disabled: true,
})

const getDefaultSelectedBoxes = (boxes: VectorSearchBox[]): string[] => boxes.map((box) => box.value)

* feat: created base wrapper for the "create index" step part of the new vector search
* integrate the tab component to serve as a base wrapper
* created a minimal implementation for the "use preset index" scenario

re #RI-7191
* used to combine multiple fields boxes in a single group

re #RI-7190
FieldTypes,
} from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Add colors mapping for tags when @redis-ui/components v38.6.0 is released
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Badge component in @redis-ui/components v38.6.0 supports custom color out of the box (docs), so we can colorize the different types of fields, once we update the dependency version.

Note: Currently @redis-ui/components and @redis-ui/styles. Unfortunately, the latter comes with some breaking changes in its latest version, so the upgrade process might need additional effort, and I won't do it as part of this task.

import { VectorSearchBox } from 'uiSrc/components/new-index/create-index-step/field-box/types'
import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Maybe transform this to a factory function, so it can be reused more easily
Copy link
Collaborator Author

@valkirilov valkirilov Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using factories (like Fishery) makes it easier to generate consistent, realistic mock data in our tests without repeating the same setup code over and over. It helps keep the tests clean and focused on what they're actually testing - not on building all the data manually every time.

For example, instead of having this static object and reusing it everywhere, we can define a factory:

const userFactory = Factory.define(() => ({
  id: '123',
  name: 'Test User',
  email: 'user@example.com',
}));

And then just use:

// Create a new example object
const user = userFactory.build();

// Or create an array of example objects
const users = userfactory.buildList(3)

// We can also easily override fields when needed
const adminUser = userFactory.build({ role: 'admin' });

This keeps tests flexible, readable, and easier to maintain as our data model grows, because we have to update the interface in a single place in the code.

PS: It's just a proposal for a future enhancement, so we can discuss it, and I would be happy to hear thoughts or alternatives!

import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Maybe transform this to a factory function, so it can be reused more easily
// TODO: Maybe make the values more dynamic with faker, so we can test more cases
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By combining Faker with our mocks/factories, we can easily generate realistic, randomized data for our test cases. This helps avoid overly hardcoded or repetitive test data and gives us more variety and confidence that our code works with different inputs - not just the same values every time.

For example:

import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';

const userFactory = Factory.define(() => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
}));

Now each test run gets slightly different (but still valid) data, which can help us catch edge cases or assumptions we might otherwise miss.

Of course, if we need consistent values for a specific test, we can always override them manually:

userFactory.build({ email: 'fixed@example.com' });

So it’s a nice balance of flexibility and realism in our tests!

PS: It's just a proposal for a future enhancement, so we can discuss it, and I would be happy to hear thoughts or alternatives!

@valkirilov valkirilov changed the title Fe/feature/ri 7190 field box component RI-7190 Field Box component Jul 11, 2025
@valkirilov valkirilov marked this pull request as ready for review July 14, 2025 06:02
@valkirilov valkirilov self-assigned this Jul 14, 2025
@pawelangelow pawelangelow force-pushed the feature/RI-6855/vector-search branch from dde0494 to 0664a81 Compare July 18, 2025 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant