Skip to content
This repository has been archived by the owner on Jun 29, 2021. It is now read-only.

Better handling of nested objects in Elasticsearch data #242

Merged
merged 6 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Apache MetaModel [WIP]

* [METAMODEL-1227] - Better handling of nested objects in Elasticsearch data
* [METAMODEL-1224] - Ensured compatibility with newer versions of PostgreSQL

### Apache MetaModel 5.3.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ private static ColumnType getColumnTypeFromMetadataField(Map<String, ?> fieldMet
final String metaDataFieldType = getMetaDataFieldTypeFromMetaDataField(fieldMetadata);

if (metaDataFieldType == null) {
return ColumnType.STRING;
// When we're dealing with a field with nested values, it doesn't have a "type", but it has "properties". In
// that case we should handle it as a field with the type "Map".
if (fieldMetadata.get("properties") != null) {
return ColumnType.MAP;
} else {
return ColumnType.STRING;
}
}

return ElasticSearchUtils.getColumnTypeFromElasticSearchType(metaDataFieldType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.metamodel.elasticsearch.rest;

import static org.apache.metamodel.elasticsearch.rest.ElasticSearchRestDataContext.DEFAULT_TABLE_NAME;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.http.HttpHost;
import org.apache.metamodel.UpdateableDataContext;
import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.Row;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.Table;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ElasticSearchRestNestedDataIT {
private static final String INDEX_NAME = "nesteddata";

private static RestHighLevelClient client;
private static UpdateableDataContext dataContext;

@Before
public void setUp() throws Exception {
final String dockerHostAddress = ElasticSearchRestDataContextIT.determineHostName();

client = ElasticSearchRestUtil
.createClient(new HttpHost(dockerHostAddress, ElasticSearchRestDataContextIT.DEFAULT_REST_CLIENT_PORT),
null, null);
client.indices().create(new CreateIndexRequest(INDEX_NAME), RequestOptions.DEFAULT);

dataContext = new ElasticSearchRestDataContext(client, INDEX_NAME);
}

@After
public void tearDown() throws IOException {
client.indices().delete(new DeleteIndexRequest(INDEX_NAME), RequestOptions.DEFAULT);
}

@Test
public void testNestedData() throws Exception {
final Map<String, Object> user = new HashMap<>();
user.put("fullname", "John Doe");
user.put("address", "Main street 1, Newville");

final Map<String, Object> userMessage = new LinkedHashMap<>();
userMessage.put("user", user);
userMessage.put("message", "This is what I have to say.");

final IndexRequest indexRequest = new IndexRequest(INDEX_NAME).id("1");
indexRequest.source(userMessage);

client.index(indexRequest, RequestOptions.DEFAULT);

final Table table = dataContext.getDefaultSchema().getTableByName(DEFAULT_TABLE_NAME);

assertThat(table.getColumnNames(), containsInAnyOrder("_id", "message", "user"));

assertEquals(ColumnType.STRING, table.getColumnByName("message").getType());
assertEquals(ColumnType.MAP, table.getColumnByName("user").getType());

dataContext.refreshSchemas();

try (final DataSet dataSet = dataContext
.query()
.from(DEFAULT_TABLE_NAME)
.select("user")
.and("message")
.execute()) {
assertEquals(ElasticSearchRestDataSet.class, dataSet.getClass());

assertTrue(dataSet.next());
final Row row = dataSet.getRow();
assertEquals("This is what I have to say.", row.getValue(table.getColumnByName("message")));

final Object userValue = row.getValue(table.getColumnByName("user"));
assertTrue(userValue instanceof Map);

@SuppressWarnings("rawtypes")
final Map userValueMap = (Map) userValue;
assertEquals("John Doe", userValueMap.get("fullname"));
assertEquals("Main street 1, Newville", userValueMap.get("address"));
}
}
}