Elastic Search 2.0 search query in main object and nested object

613 Views Asked by At

This is my model:

public class Student
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [Nested]
    public List<Subject> Subjects { get; set; }
}

public class Subject
{
    public int Id { get; set; }
    public string SubjectName { get; set; }        
}

Mapping:

mappings: {
  student: {
    properties: {
      firstName: {
        type: "string"
      },
      id: {
        type: "integer"
      },
      lastName: {
        type: "string"
      },
      subjects: {
        type: "nested",
        properties: {
          id: {
            type: "integer"
          },
          subjectName: {
            type: "string"
          }
       } 
    }
 }
}
}

To search inside the nested object (Subject) I use following code and it returns the values correctly.

var searchResponse = client.Search<Student>(s => s    
.Query(q => q
    .Nested(n => n
        .Path(p => p.VolunteerTasks)
        .Query(nq => nq.Match(m => m
            .Query(searchText).Field("subjects.subjectName"))))));
 return searchResponse.Documents;

But I want to search with the same searchText for student.firstName, student.lastName and subjects.subjectName. How can I do that?

3

There are 3 best solutions below

0
jaspreet chahal On BEST ANSWER

You need to add a should clause with a match query on first name and last name and a nested query on subject name. You cannot club nested and non nested query in a single match or multi-match query

var searchResponse = _elasticClient.Search<AssociateProfile>(s => s
                     .Query(q => q
                                 .Bool(b=>b
                                             .Should(
                                                        sh => sh.Match(m => m.Query(searchText).Field("student.firstName")),
                                                        sh => sh.Match(m => m.Query(searchText).Field("student.lastName")),
                                                         sh => sh.Nested(n => n
                                                                               .Path(p => p.VolunteerTasks)                                                                                           .Query(nq => nq.Match(m => m                                                                                           .Query(searchText).Field("subjects.subjectName")))
                                                                                    )

                                                                   )
                                                    )
                                              ));
0
Amit On

Providing answer in form of REST API, which you can convert it into c# format, As I am not familiar with its syntax and it would be helpful for people who are not looking for language-specific answers.

Tested this with your sample data and below is the working solution.

Index Def

{
    "student": {
        "properties": {
            "firstName": {
                "type": "string"
            },
            "id": {
                "type": "integer"
            },
            "lastName": {
                "type": "string"
            },
            "subjects": {
                "type": "nested",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "subjectName": {
                        "type": "string"
                    }
                }
            }
        }
    }
}

**Index sample doc which doesn't have opster either subject and firstname **

{
    "firstName": "Isuru",
    "lastName": "foo",
    "id": 1,
    "subjects": [
        {
            "id": 100,
            "subjectName": "math"
        },
        {
            "id": 101,
            "subjectName": "opster"
        }
    ]
}

Index another doc which doesn't have opster in any subject name

{
    "firstName": "opster",
    "lastName": "tel aviv",
    "id": 1,
    "subjects": [
        {
            "id": 100,
            "subjectName": "math"
        },
        {
            "id": 101,
            "subjectName": "science"
        }
    ]
}

Search query, please change must to should according to your requirements

{
    "query": {
        "bool": {
            "should": [    --> note
                {
                    "match": {
                        "firstName": "opster"
                    }
                },
                {
                    "nested": {
                        "path": "subjects",
                        "query": {
                            "bool": {
                                "must": [   -->note
                                    {
                                        "match": {
                                            "subjects.subjectName": "opster"
                                        }
                                    }
                                ]
                            }
                        }
                    }
                }
            ]
        }
    }
}

Search result

"hits": [
            {
                "_index": "nested",
                "_type": "student",
                "_id": "1",
                "_score": 0.39103588,
                "_source": {
                    "firstName": "opster",
                    "lastName": "tel aviv",
                    "id": 1,
                    "subjects": [
                        {
                            "id": 100,
                            "subjectName": "math"
                        },
                        {
                            "id": 101,
                            "subjectName": "science"
                        }
                    ]
                }
            },
            {
                "_index": "nested",
                "_type": "student",
                "_id": "2",
                "_score": 0.39103588,
                "_source": {
                    "firstName": "Isuru",
                    "lastName": "foo",
                    "id": 1,
                    "subjects": [
                        {
                            "id": 100,
                            "subjectName": "math"
                        },
                        {
                            "id": 101,
                            "subjectName": "opster"
                        }
                    ]
                }
            }
        ]
0
Amit On

Based on the comment from the OP, Adding an answer which provides the partial match. like if the document contains Science and technology and searching for techno should also come. Note using the custom edge-n-gram analyzer for achieving this requirement.

Index def

{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 10
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "autocomplete", 
        "search_analyzer": "standard" 
      }
    }
  }
}

Index sample doc

{
    "title" : "Science and technology"
}

Search query

{
    "query" :{
        "match" :{
            "title" :"techno"
        }
    }
}

Search result

 "hits": [
            {
                "_index": "edge",
                "_type": "_doc",
                "_id": "1",
                "_score": 0.44104567,
                "_source": {
                    "title": "Science and technology"
                }
            }
        ]