Skip to content

Commit

Permalink
Support full-text index decorator in babel plugin (#6218)
Browse files Browse the repository at this point in the history
* Support full-text index decorator in babel plugin

- Check for `index` decorator calls that are passed the string "full-text" and subsequently add the `{ indexed: "full-text" }` property to the schema.
- Update README with description and example of indexing a string field by full text
- Update tests to verify schemas with full-text indexing are properly generated

* Remove check for index decorator parameter

Removes check that the specific string "full-text" is the only allowable parameter to the `@index` decorator. New behavior is to allow any string to be passed through to the schema. Invalid values in the schema will throw at runtime, alerting user to possible errors.
  • Loading branch information
atdyer committed Nov 20, 2023
1 parent 6dcc3b1 commit 30592da
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
13 changes: 8 additions & 5 deletions packages/babel-plugin/README.md
Expand Up @@ -206,10 +206,10 @@ Note that use of decorators requires using the `@babel/plugin-proposal-decorator

This table shows the available decorators:

| Decorator | Parameters | Notes |
| --------- | ----------------------------- | ---------------------------------------------------------------------------------------------------- |
| `index` | none | Specifies that the decorated property should be indexed by Realm. |
| `mapTo` | `(realmPropertyName: string)` | Specifies that the decorated property should be stored as `realmPropertyName` in the Realm database. |
| Decorator | Parameters | Notes |
|-----------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `index` | none or `("full-text")` | Specifies that the decorated property should be indexed by Realm. Providing the string "full-text" specifies that the property should be indexed for full-text search by Realm. |
| `mapTo` | `(realmPropertyName: string)` | Specifies that the decorated property should be stored as `realmPropertyName` in the Realm database. |

The example below shows both decorators in use:

Expand All @@ -219,8 +219,11 @@ import { mapTo, index } from "@realm/babel-plugin";

export class Task extends Realm.Object {
_id!: Realm.BSON.ObjectId;
// Add an index to the `description` property
// Add an index to the `assignee` property
@index
assignee!: string;
// Specify that the `description` property should be indexed for full-text search
@index("full-text")
description!: string;
// Specify that the `isComplete` property should be stored as `complete` in the Realm database
@mapTo("complete")
Expand Down
33 changes: 33 additions & 0 deletions packages/babel-plugin/src/index.test.ts
Expand Up @@ -390,6 +390,39 @@ describe("Babel plugin", () => {
expect(transformCode).not.toContain("_applyDecoratedDescriptor");
});

it('handles `@index("full-text")` decorators', () => {
const transformCode = transformProperty(`@index("full-text") name: Realm.Types.String;`);
const parsedSchema = extractSchema(transformCode);

expect((parsedSchema?.properties.name as PropertySchema).indexed).toEqual("full-text");
})

it('handles `@index("full-text")` decorators from the Realm import', () => {
const transformCode = transform({
source: `import Realm, { Types, BSON, List, Set, Dictionary, Mixed } from "realm";
export class Person extends Realm.Object { @Realm.index("full-text") name: Realm.Types.String; }`,
});
const parsedSchema = extractSchema(transformCode);

expect((parsedSchema?.properties.name as PropertySchema).indexed).toEqual("full-text");
});

it('ignores `@index("full-text")` decorators not imported from `realm`', () => {
const transformCode = transform({
source: `import Realm, { Types, BSON, List, Set, Dictionary, Mixed } from "realm";
export class Person extends Realm.Object { @index("full-text") name: Realm.Types.String; }`,
});
const parsedSchema = extractSchema(transformCode);

expect((parsedSchema?.properties.name as PropertySchema).indexed).toBeUndefined();
});

it('removes `@index("full-text")` decorators from the source', () => {
const transformCode = transformProperty(`@index("full-text") name: Realm.Types.String;`);
// This is what Babel outputs for transformed decorators
expect(transformCode).not.toContain("_applyDecoratedDescriptor");
});

it("handles `@mapTo` decorators", () => {
const transformCode = transformProperty(`@mapTo("rename") name: Realm.Types.String;`);
const parsedSchema = extractSchema(transformCode);
Expand Down
12 changes: 12 additions & 0 deletions packages/babel-plugin/src/plugin/index.ts
Expand Up @@ -290,6 +290,13 @@ function visitRealmClassProperty(path: NodePath<types.ClassProperty>) {
}
const index = Boolean(indexDecorator);

const indexDecoratorCall = findDecoratorCall(decoratorsPath, "index");
const indexCall =
indexDecoratorCall
&& types.isStringLiteral(indexDecoratorCall.callExpression.arguments[0])
? indexDecoratorCall.callExpression.arguments[0].value
: undefined;

const mapToDecorator = findDecoratorCall(decoratorsPath, "mapTo");
const mapTo =
mapToDecorator && types.isStringLiteral(mapToDecorator.callExpression.arguments[0])
Expand All @@ -299,6 +306,7 @@ function visitRealmClassProperty(path: NodePath<types.ClassProperty>) {
// Remove the decorators from the final source as they are only for schema annotation purposes.
// Decorator implementations will throw to prevent usage outside of the plugin.
if (indexDecorator) indexDecorator.remove();
if (indexDecoratorCall) indexDecoratorCall.decoratorNode.remove();
if (mapToDecorator) mapToDecorator.decoratorNode.remove();

if (keyPath.isIdentifier()) {
Expand Down Expand Up @@ -336,6 +344,10 @@ function visitRealmClassProperty(path: NodePath<types.ClassProperty>) {
properties.push(types.objectProperty(types.identifier("indexed"), types.booleanLiteral(true)));
}

if (indexCall) {
properties.push(types.objectProperty(types.identifier("indexed"), types.stringLiteral(indexCall)))
}

if (mapTo) {
properties.push(types.objectProperty(types.identifier("mapTo"), types.stringLiteral(mapTo)));
}
Expand Down

0 comments on commit 30592da

Please sign in to comment.