# Elasticsearch NEST query on a list of attachments

**URL:** <https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619>\
**Category:** Elasticsearch\
**Created:** [November 6, 2018, 8:46pm UTC](https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619 "2018-11-06T20:46:03Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![alina.frey](https://avatars.discourse-cdn.com/v4/letter/a/c57346/32.png) [@alina.frey](https://discuss.elastic.co/u/alina.frey)\
**Post date:** [November 6, 2018, 8:46pm UTC](https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619/1 "2018-11-06T20:46:03Z")

</div>

- Using ingest-attachment plugin.

- Elasticsearch version: 5.5.1

- NEST: 5.5

- My goal is to be able to search the Content of a list of documents called PersonDocuments attached to IndexablePersonModel, and return the persons that have documents that contain the query term.

- PersonDocuments is a list of IndexablePersonDocument on the IndexablePersonModel

```auto
public IEnumerable<IndexablePersonDocument> PersonDocuments { get; set; }

```

- And one of the attributes of IndexablePersonDocument is string Content.

- How do I query on the PersonDocuments?

- Here is the query that I have so far:

```auto
QueryContainer Query(QueryContainerDescriptor < IndexablePersonModel > q) {
	var returnQuery = q
		 .Match(m => m
			.Field(a => a.PersonDocuments.FirstOrDefault().Content)
			.Boost(SearchConstants.Boosts.XXXLarge)
			.Query(Form.Query))
		 || q.FunctionScore(fs => fs
			.MaxBoost(SearchConstants.Boosts.Large)
			.Functions(ff => ff
				.FieldValueFactor(fvf => fvf
					.Field(p => p...)
					.Factor(0.0001)))
			.Query(query => query
				.MultiMatch(m => m
					.Fields(f => f...)
					.Operator(Operator.And)
					.Query(Form.Query))));

	return returnQuery;
}

```

- The line with the problem is:

```auto
.Field(a => a.PersonDocuments.FirstOrDefault().Content)

```

- I don't know how to loop through the list PersonDocuments.

- The following is the index that I created on persons:

```auto
{
	"person-index": {		
		"mappings": {
			"person": {
				"properties": {
					"name": {
						"type": "text",
						"fields": {
							"keyword": {
								"type": "text",
								"analyzer": "person-name-keyword"
							},
							"raw": {
								"type": "keyword"
							}
						},
						"analyzer": "person-name-analyzer"
					},
					"personDocuments": {
						"type": "nested",
						"properties": {
							"attachment": {
								"properties": {
									"author": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"content": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"content_length": {
										"type": "long"
									},
									"content_type": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"date": {
										"type": "date"
									},
									"detect_language": {
										"type": "boolean"
									},
									"indexed_chars": {
										"type": "long"
									},
									"keywords": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"language": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"name": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									},
									"title": {
										"type": "text",
										"fields": {
											"keyword": {
												"type": "keyword",
												"ignore_above": 256
											}
										}
									}
								}
							},
							"content": {
								"type": "text",
								"fields": {
									"keyword": {
										"type": "keyword",
										"ignore_above": 256
									}
								}
							},
							"id": {
								"type": "integer"
							},
							"name": {
								"type": "text",
								"analyzer": "document-path-analyzer"
							},
							"path": {
								"type": "text",
								"fields": {
									"keyword": {
										"type": "keyword",
										"ignore_above": 256
									}
								}
							}
						}
					}
				}
			}
		},
		"settings": {
			...
		}
	}
}

```

---

<div class="post-metadata">

**Author:** ![forloop](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/forloop/32/9021_2.png) [@forloop](https://discuss.elastic.co/u/forloop)\
**Post date:** [November 7, 2018, 10:05am UTC](https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619/2 "2018-11-07T10:05:54Z")

</div>

> [@alina.frey](#):
>
> - The line with the problem is:
> 
> ```auto
> .Field(a => a.PersonDocuments.FirstOrDefault().Content)
> 
> ```
> 
> - I don't know how to loop through the list PersonDocuments.

The lambda expression here is used to construct the path to the correct field, in a strongly typed fashion; the resulting serialized output for that expression would be

```json
"field": "personDocuments.content"

```

Where the indexed field is a collection of values, such as in the case of

```auto
public IEnumerable<IndexablePersonDocument> PersonDocuments { get; set; }

```

The query will be executed across the values in the collection for each document.

You'll notice that the mapping for `personDocuments` includes the properties of only a single `personDocument`. This is because Elasticsearch has no concept of an ["array" datatype](https://www.elastic.co/guide/en/elasticsearch/reference/current/array.html). As far as it is concerned, you can index a single `personDocument`, multiple `personDocument` instances, or none into the `personDocuments` field. What will be mapped back to your POCO however will be the original JSON that you indexed. Your POCO models the `personDocuments` field as a collection, so your model/application always expects a collection (including null or empty) back from Elasticsearch.

A couple of points though

1. `personDocuments` is mapped as a [`nested` data type](https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html), so a query on a field of `personDocuments` needs to be a `nested` query i.e. you need to put the `Match` query on the `Content` field inside of [a `Nested` query](https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/nested-query-usage.html)

2. A collection of the top level `person` documents will be returned, and not `personDocuments`. If you wish to get the matching `personDocuments`, take a look at [`Inner hits`](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html).

---

<div class="post-metadata">

**Author:** ![alina.frey](https://avatars.discourse-cdn.com/v4/letter/a/c57346/32.png) [@alina.frey](https://discuss.elastic.co/u/alina.frey)\
**Post date:** [November 7, 2018, 5:01pm UTC](https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619/3 "2018-11-07T17:01:31Z")

</div>

- I modified the query to the following, and still getting zero hits. Please advise.

```auto
QueryContainer Query(QueryContainerDescriptor < IndexablePersonModel > q) {
	var returnQuery = q
		.FunctionScore(fs => fs...)
		.Nested(n => n
			.Boost(SearchConstants.Boosts.XXXLarge)
			.InnerHits(i => i.Explain())
			.Path(p => p.PersonDocuments)
			.Query(nq => +nq
				.Bool(b => b
					.Should(
						s => s.Term(p => p.PersonDocuments.First().Attachment.Content.Suffix(SearchConstants.Keyword), <query-term>)
					).MinimumShouldMatch(MinimumShouldMatch.Fixed(1))
				)
			)
			.IgnoreUnmapped()
		);
					
	return returnQuery;
}

```

- When I do a search from Postman with "query-term":

```auto
POST: http://localhost:9200/index-name/_search
BODY:
{
    "query": {
        "query_string": {
            "query": "query-term"
        }
    }
}

```

- I get the following output:

```auto
{
    "took": 61,
    "timed_out": false,
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "hits": {
        "total": 4,
        "max_score": 1,
        "hits": [
            {
                "_index": "<index-name>",
                "_type": "person",
                "_id": "72802",
                "_score": 1,
                "_source": {                    
                    "attribute1": {
                        "input": [
                            ...
                        ],
                        "weight": 0
                    },
                    "attribute2": false,
					...
                    
                    "personDocuments": [
                        {
                            "attachment": {
                                "content_type": "text/plain; charset=ISO-8859-1",
                                "language": "en",
                                "content": "<query-term>",
                                "content_length": 7
                            },
                            "name": "<name-of-the-file>.pdf",
                            "id": 0,
                            "content": "<base64-content>"
                        }
                    ]
                }
            },
            ...
            ...
            ...
        ]    
    }
}

```

- So, I know I am retrieving the objects in Postman, but I don't know how to match that in C# NEST code.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [December 5, 2018, 5:01pm UTC](https://discuss.elastic.co/t/elasticsearch-nest-query-on-a-list-of-attachments/155619/4 "2018-12-05T17:01:50Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
