2

Say I got an ElasticSearch up and running with and Blog object.

 public class Blog
 {
     [ElasticProperty(Name = "guid", Index = FieldIndexOption.NotAnalyzed, Type = FieldType.String)]
     public Guid? Guid { get; set; }

     [ElasticProperty(Name = "title", Index = FieldIndexOption.Analyzed, Type = FieldType.String)]
    public string Title { get; set; } = "";

     [ElasticProperty(Name = "body", Index = FieldIndexOption.Analyzed, Type = FieldType.String)]
     public string Body { get; set; } = "";

     [ElasticProperty(Name = "publishedDate", Index = FieldIndexOption.Analyzed, Type = FieldType.Date)]
     public DateTime PublishedDate { get; set; }
}

Now I would only like to return a subset of the properties into a new class

 public class BlogListItem
 {
       public static Expression<Func<Blog, object>> Selector = e => new BlogListItem
        {
            Title =  e.Title,
            PublishedDate = e.PublishedDate,
        };

        public string Title { get; set; }
        public DateTime PublishedDate { get; set; }
 }

Normally I work with Entity Framework where I would write an Selector like in the BlogListItem class, but I'm finding it hard to find any information about doing this in ElasticSearch with NEST

var res = elastic.Search<Blog>(s => s
           .From(0)
           .Size(3)
           .Index(blogIndex)
           .Query(q => q.MatchAll())
           .Sort(o => o.OnField(p => p.PublishedDate))
           .Fields(BlogListItem.Selector)
           );

  var result = res.Hits.Select(e => e.Source).ToList();

This returns the correct number of Hits, but with an null source and I can't figure out where to find the returned properties.

Solution 1 I found an alternative solution, but would like input if this is a good solution.

var res2 = elastic.Search<Blog, BlogListItem>(s => s
             .From(0)
             .Size(3)
             .Index(blogIndex)
             .Query(q => q.MatchAll())
             .Sort(o => o.OnField(p => p.PublishedDate))
             );

List<BlogListItem> resultList = res2.Hits.Select(hit => hit.Source).ToList();

This gives me the correct object returned, but I don't have any control about mapping, and I'm not sure if it returns all properties and then do the mapping.

Solution 2.5 In this solution i have updated my BlogListItem with a new Selector.

    public class BlogListItem
    {
        public static SearchSourceDescriptor<Blog> Selector(SearchSourceDescriptor<Blog> sr)
        {
            return sr.Include(fi => fi.Add(f => f.Title));
        }

        [ElasticProperty(Name = "title")]        
        public string TitleNewName { get; set; }
        public DateTime PublishedDate { get; set; }
    }

And then my elasticSearch code

var res3 = elastic.Search<Blog, BlogListItem>(s => s
                .From(0)
                .Size(3)
                .Index(blogIndex)
                .Query(q => q.MatchAll())
                .Sort(o => o.OnField(p => p.PublishedDate))                   
                .Source(BlogListItem.Selector)
                );

            List<BlogListItem> resultList = res3.Hits.Select(hit => hit.Source).ToList();

Now this limits the properties returned so I only get Title and the PublishedDate is null and I know have control on the mapping thanks to

[ElasticProperty(Name = "title")]

Still needs to verify if this is the correct way of working with ElasticSearch.

This produces the following Json

{
  "from": 0,
  "size": 3,
  "sort": [
    {
      "publishedDate": {}
    }
  ],
  "_source": {
    "include": [
      "title"      
    ]
  },
  "query": {
    "match_all": {}
  }
}
Anders Gulbæk
  • 349
  • 1
  • 5
  • 16

1 Answers1

1

There are some problems with your code.

Using .Fields(BlogListItem.Selector) returns a field called "publishedDate.title" which of course is wrong. I'm not sure how to use the Expression syntax for mentioning fields so I'll not try to fix it. I found this bug by printing the request JSON. Look at my answer to another question on SO to know how to debug Nest queries by printing the request JSON. May be you can fix the Expression syntax yourself using this trick :)

I fixed Fields() using the syntax I'm best familiar with:

.Fields(f => f
    .Add(t => t.Title)
    .Add(t => t.PublishedDate)

Even with this fix, you'll find that source is null. We come to the next problem. If you provide "fields" option in the search request, "_source" will not be present in the response hits. This is Elasticsearch's behaviour and has nothing to do with Nest. In this case, you have to rely on Fields.FieldValuesDictionary instead of Source:

var result = res.Hits.Select(e => e.Fields.FieldValuesDictionary).ToList();

Then you can build BlogListItem objects from the result above.

Community
  • 1
  • 1
bittusarkar
  • 6,247
  • 3
  • 30
  • 50
  • do you know anything about the Source propertie? It just seemds like alot of work to get the properties back from a FieldValuesDictionay – Anders Gulbæk Jan 03 '16 at 20:23
  • I think you've done a good job with source filtering. That is the best solution (solution 2.5) I guess. – bittusarkar Jan 04 '16 at 05:30