4

I'm trying to use NEST to return various objects derived from a common subclass.

In this example I have a base class called "Person" and then I have derived classes called "Firefighter" and "Teacher". Instances are stored in an index called "people".

I'd like to do a search on my index and return a mix of Firefighters and Teachers, but the best I can get back is a list of Persons.

The documentation calls for the use of ISearchResponse.Types, but I don't see that this function exists. This post, here on StackOverflow, also references the .Types function but I just don't see it as an option.

Here's an example of the problem I'm trying to solve.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nest;
using Elasticsearch;
using Elasticsearch.Net;

namespace ElasticSearchTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var myTeacher = new Teacher { id="1", firstName = "Steve", lastName = "TheTeacher", gradeNumber = 8 };
            var myFiregighter = new Firefighter { id="2", firstName = "Frank", lastName = "TheFirefighter", helmetSize = 12 };

            SingleNodeConnectionPool esPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
            ConnectionSettings esSettings = new ConnectionSettings(esPool);
            esSettings.DefaultIndex("people");
            Nest.ElasticClient esClient = new ElasticClient(esSettings);

            esClient.DeleteIndex("people");
            esClient.Index<Teacher>(myTeacher);
            esClient.Index<Firefighter>(myFiregighter);

            System.Threading.Thread.Sleep(2000);

            ISearchResponse<Person> response = esClient.Search<Person>(s => s   
                .AllTypes()
                .MatchAll()
                );

            foreach (Person person in response.Documents)
                Console.WriteLine(person);
        }

        public class Person
        {
            public string id { get; set; }
            public string firstName { get; set; }
            public string lastName { get; set; }
            public override string ToString() { return String.Format("{0}, {1}", lastName, firstName);}
        }
        public class Firefighter : Person
        {
            public int helmetSize { get; set; }
        }
        public class Teacher : Person {
            public int gradeNumber { get; set; } 
        }
    }
}

The data appears to be structured correctly inside Elasticsearch:

{
    took: 1,
    timed_out: false,
    _shards: {
        total: 5,
        successful: 5,
        failed: 0
    },
    hits: {
        total: 2,
        max_score: 1,
        hits: [{
            _index: "people",
            _type: "firefighter",
            _id: "2",
            _score: 1,
            _source: {
                helmetSize: 12,
                id: "2",
                firstName: "Frank",
                lastName: "TheFirefighter"
            }
        }, {
            _index: "people",
            _type: "teacher",
            _id: "1",
            _score: 1,
            _source: {
                gradeNumber: 8,
                id: "1",
                firstName: "Steve",
                lastName: "TheTeacher"
            }
        }]
    }
}

How do I get NEST to return a mixed list of instances derived from a base class?

Many thanks!

-Z

Community
  • 1
  • 1
zorlack
  • 664
  • 2
  • 7
  • 26

1 Answers1

4

NEST supports Covariant results and in NEST 2.x, the naming of the method on ISearchResponse<T> was changed to .Type() to align with the route parameter that it represents in the URI when making the request to Elasticsearch (.Type() can be one or more types).

An example of Covariant results for NEST 2.x is

static void Main(string[] args)
{
    var myTeacher = new Teacher { id = "1", firstName = "Steve", lastName = "TheTeacher", gradeNumber = 8 };
    var myFiregighter = new Firefighter { id = "2", firstName = "Frank", lastName = "TheFirefighter", helmetSize = 12 };

    SingleNodeConnectionPool esPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    ConnectionSettings esSettings = new ConnectionSettings(esPool);
    esSettings.DefaultIndex("people");
    Nest.ElasticClient esClient = new ElasticClient(esSettings);

    if (esClient.IndexExists("people").Exists)
    {
        esClient.DeleteIndex("people");
    }

    esClient.Index<Teacher>(myTeacher, i => i.Refresh());
    esClient.Index<Firefighter>(myFiregighter, i => i.Refresh());

    // perform the search using the base type i.e. Person
    ISearchResponse<Person> response = esClient.Search<Person>(s => s
        // Use .Type to specify the derived types that we expect to return
        .Type(Types.Type(typeof(Teacher), typeof(Firefighter)))
        .MatchAll()
    );

    foreach (Person person in response.Documents)
        Console.WriteLine(person);
}

public class Person
{
    public string id { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
    public override string ToString() { return String.Format("{0}, {1}", lastName, firstName); }
}
public class Firefighter : Person
{
    public int helmetSize { get; set; }
}
public class Teacher : Person
{
    public int gradeNumber { get; set; }
}
Russ Cam
  • 124,184
  • 33
  • 204
  • 266