# Migrating @FieldBridge to v6.1 (Map case)

**URL:** <https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519>\
**Category:** Hibernate Search\
**Created:** [July 25, 2022, 2:45pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519 "2022-07-25T14:45:52Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 25, 2022, 2:45pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/1 "2022-07-25T14:45:52Z")

</div>

Hi there!

I’m still trying to migrate @FieldBridge annotations, but in this case refers to a Map\<String, Integer\> property.

I’ve defined the property this way:

```auto
@PropertyBinding(binder = @PropertyBinderRef(type = MapValueBinder.class))
@GenericField(searchable = Searchable.YES, projectable = Projectable.YES, name = "permission")
protected Map<String, Integer> permissions = new HashMap<>();

```

Using this custom PropertyBinder:

```auto
public class MapValueBinder implements PropertyBinder {
	private static final Logger log = LoggerFactory.getLogger(MapValueBinder.class);

	@Override
	public void bind(PropertyBindingContext context) {
		context.dependencies().useRootOnly();
		IndexSchemaElement schemaElement = context.indexSchemaElement();
		IndexSchemaObjectField userMetadataField = schemaElement.objectField("permissionBinder");
		userMetadataField.fieldTemplate("permissionTemplate", IndexFieldTypeFactory::asString);

		context.bridge(Map.class, new MapValueBridge(userMetadataField.toReference()));
	}

	@SuppressWarnings("rawtypes")
	private static class MapValueBridge implements PropertyBridge<Map> {
		private final IndexObjectFieldReference userMetadataFieldReference;

		private MapValueBridge(IndexObjectFieldReference userMetadataFieldReference) {
			this.userMetadataFieldReference = userMetadataFieldReference;
		}

		@Override
		public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
			@SuppressWarnings("unchecked")
			Map<String, Integer> permissions = (Map<String, Integer>) bridgedElement;
			DocumentElement indexedUserMetadata = target.addObject(userMetadataFieldReference);

			for (Map.Entry<String, Integer> elto : permissions.entrySet()) {
				if (elto.getValue() > 10) {
					indexedUserMetadata.addValue("myFieldName", elto.getKey());
				}
			}
		}
	}
}

```

Looking at Lucene indexe, what I get is:

- **permission** (the map entry value)
- **permissionBinder.myFieldName** (my custom value)

But I want several “permission” fields with a given value, not the one from the map entry. How can I do that?

Thanks in advence!

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [July 26, 2022, 6:21am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/2 "2022-07-26T06:21:04Z")

</div>

Hi,

If you don’t want to put the fields on the `permissionBinder` object field, but on its parent instead… just remove the object field, and put the fields on the parent?

```diff
 public class MapValueBinder implements PropertyBinder {
 	private static final Logger log = LoggerFactory.getLogger(MapValueBinder.class);
 
 	@Override
 	public void bind(PropertyBindingContext context) {
 		context.dependencies().useRootOnly();
 		IndexSchemaElement schemaElement = context.indexSchemaElement();
- IndexSchemaObjectField userMetadataField = schemaElement.objectField("permissionBinder");
- userMetadataField.fieldTemplate("permissionTemplate", IndexFieldTypeFactory::asString);
+ schemaElement.fieldTemplate("permissionTemplate", IndexFieldTypeFactory::asString);
 
- context.bridge(Map.class, new MapValueBridge(userMetadataField.toReference()));
+ context.bridge(Map.class, new MapValueBridge());
 	}
 
 	@SuppressWarnings("rawtypes")
 	private static class MapValueBridge implements PropertyBridge<Map> {
 		private final IndexObjectFieldReference userMetadataFieldReference;
 
 		private MapValueBridge(IndexObjectFieldReference userMetadataFieldReference) {
 			this.userMetadataFieldReference = userMetadataFieldReference;
 		}
 
 		@Override
 		public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
 			@SuppressWarnings("unchecked")
 			Map<String, Integer> permissions = (Map<String, Integer>) bridgedElement;
- DocumentElement indexedUserMetadata = target.addObject(userMetadataFieldReference);
 
 			for (Map.Entry<String, Integer> elto : permissions.entrySet()) {
 				if (elto.getValue() > 10) {
- indexedUserMetadata.addValue("myFieldName", elto.getKey());
+ target.addValue("myFieldName", elto.getKey());
 				}
 			}
 		}
 	}
 }

```

---

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 26, 2022, 8:34am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/3 "2022-07-26T08:34:05Z")

</div>

Thanks, it worked but when there is more tha one element in the map, an exception is thrown:

```auto
HSEARCH600074: Multiple values assigned to field 'myFieldName': this field is single-valued. Declare the field as multi-valued in order to allow this.

```

This migration is being much more complex than expected 😓

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [July 26, 2022, 8:41am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/4 "2022-07-26T08:41:49Z")

</div>

> [@Monkiki](#):
>
> when there is more tha one element in the map, an exception is thrown

The solution is in the error message:

> Declare the field as multi-valued in order to allow this.

See [Hibernate Search 7.0.0.Final: Reference Documentation](https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#mapper-orm-bridge-index-field-dsl-multi-valued-fields)

---

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 26, 2022, 11:01am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/5 "2022-07-26T11:01:07Z")

</div>

Ok, I’ve solved this way:

```auto
schemaElement.fieldTemplate("permissionTemplate", f -> f.asString()
	.searchable(Searchable.YES).projectable(Projectable.YES))
	.multiValued();

```

But I’ve another doubt: in Lucene document I have now both fields: **permission** and **myFieldName**. And I’m not sure how I can avoid adding the **permission** field. If I change **myFielName** to **permission** an error is thrown, so the only way I see is defining the field this way:

```auto
@PropertyBinding(binder = @PropertyBinderRef(type = MapValueBinder.class))
@GenericField(searchable = Searchable.NO, projectable = Projectable.NO)
protected Map<String, Integer> permissions = new HashMap<>();

```

And seems to work, but not sure if this has other undeseable implications. Even this also seems to work and not sure which is the best approach:

```auto
@PropertyBinding(binder = @PropertyBinderRef(type = MapValueBinder.class))
protected Map<String, Integer> permissions = new HashMap<>();

```

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [July 26, 2022, 12:39pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/6 "2022-07-26T12:39:17Z")

</div>

I don’t even know what you’re trying to do, so it’s hard to give you advice 🙂

Why do use use both `@PropertyBinding` and `@GenericField` in the first place?

You do realize that the only effect of your `@GenericField` annotation is to index the map values in a `permissions` field? I’m not sure what’s the point of that.

---

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 26, 2022, 2:36pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/7 "2022-07-26T14:36:42Z")

</div>

Well, I’m still trying to understand this new version of Hibernate Search because has many, many changes. In v5.6 I was using:

```auto
@Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES)
@FieldBridge(impl = MapFieldBridge.class)
protected Map<String, Integer> permissions = new HashMap<>();

```

I implemented a custom **MapFieldBridge** which added other values to the Lucene document, and worked pretty fine. It was much easier than now. I was trying to reproduce the same behaviour with the new version. I know right now is more search engine independent, but things are now much complex.

In short, I wanted to add values from the information stored in the Map (according to certain logic) using a give name, in this case “myFieldName” (as many as elements in the Map which matches a certain criteria).

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [July 26, 2022, 2:45pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/8 "2022-07-26T14:45:14Z")

</div>

> [@Monkiki](#):
>
> In v5.6 I was using:
> 
> ```auto
> @Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES)
> @FieldBridge(impl = MapFieldBridge.class)
> protected Map<String, Integer> permissions = new HashMap<>();
> 
> ```

Considering you’re only creating fields using your custom bridge here, In Hibernate Search 6 you most likely only need `@PropertyBinding`, not `@GenericField`.

It might not be obvious in Hibernate Search 5, but the `@FieldBridge` annotation does nothing on its own: it just configures the `@Field` annotation further.

To understand Hibernate Search 6 better, I suggest you have a look at the documentation.  
The table in [Bridge basics](https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#section-custom-bridges) might help.

If necessary, for more general topics, there is an extensive migration guide here: [https://docs.jboss.org/hibernate/search/6.0/migration/html\_single/](https://docs.jboss.org/hibernate/search/6.0/migration/html_single/)

> [@Monkiki](#):
>
> I know right now is more search engine independent, but things are now much complex.

Things were already complex: hibernate Search 5 made assumptions that made things look simpler, but could often result in wrong behavior (missing reindexing, wrong field types) if you didn’t know better, or even limitations (no faceting for bridge-created fields, …).

So no, this “added” complexity is not just about Elasticsearch support.

---

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 26, 2022, 3:42pm UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/9 "2022-07-26T15:42:05Z")

</div>

I’ve already read the [Hibernate Search 6.0.9.Final: Migration Guide from 5.11](https://docs.jboss.org/hibernate/search/6.0/migration/html_single/) and helped, but I have some cases which are out of the scope of this guide, or are not very intuitive like `@FieldBridge` migration.

By the way, is there any project with working code samples? I mean a simple Hibernate Search project where people can take a look and understand better how to use this new version improvements properly. I know the documentation is great but working samples would make it better.

Thanks a lot!

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [July 27, 2022, 6:21am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/10 "2022-07-27T06:21:51Z")

</div>

> [@Monkiki](#):
>
> By the way, is there any project with working code samples?

There’s a showcase [here](https://github.com/hibernate/hibernate-search/tree/main/integrationtest/showcase/library), as [linked from the getting started guide](https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#_whats_next).

There are also [various resources](https://hibernate.org/search/more-resources/) listed on the website, most of which include a link to a GitHub repository.

Otherwise, you can find a few open-source projects relying on Hibernate Search here: [Network Dependents · hibernate/hibernate-search · GitHub](https://github.com/hibernate/hibernate-search/network/dependents) I’m afraid most applications using Hibernate Search are closed-source, though…

---

<div class="post-metadata">

**Author:** ![Monkiki](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/monkiki/32/850_2.png) [@Monkiki](https://discourse.hibernate.org/u/Monkiki)\
**Post date:** [July 27, 2022, 8:23am UTC](https://discourse.hibernate.org/t/migrating-fieldbridge-to-v6-1-map-case/6519/11 "2022-07-27T08:23:40Z")

</div>

Great, thanks for the links!
