0
votes

I am using ES 5. With Nest lib on C#.

I have two entities in my model, Contact and events. I have a requirement in which I have to get all contact than have more than N events (very similar to a having query on SQL). Contact are event parents so I can filter using a parent/child strategy.

I was able to get the aggregations for the contacts by I am not able to filter the contact by those aggreagations.

I did something like this:

var queryResult = client.Search<Contact>(s => s
            .Index("contact*")                
            .Query(q => q
                ...
            )
            .Aggregations(a => a
                .Children<Event>("filter_event", ca => ca.Aggregations(ca2 => ca2
                    .Filter("filter1", f => f.Filter(fq => fq.Term(t => t.Field(tf => tf.EventName).Value("Event1")))
                        .Aggregations(fa => fa
                            .Terms("filter1Contacts", v => v.Field(faf => faf.EventContactGuid).Size(int.MaxValue).MinimumDocumentCount(5))
                        )
                    )                        
            )))
        );

With this code I was able to get the aggregations for only those contacts with more than 5 events, but I did not find a way to filter my contact based on those aggregation results.

There is a way to do this in ES 5?

1
To be clear - you want to return Contact documents that have more than N child Event documents?Russ Cam

1 Answers

1
votes

You use a has_child query for this, here's an example to try in Linqpad

void Main()
{
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    var defaultIndex = "orders";
    var connectionSettings = new ConnectionSettings(pool)
            .DefaultIndex(defaultIndex)
            .InferMappingFor<Order>(m => m
                .IdProperty(f => f.Customer)
            )
            .PrettyJson()
            .DisableDirectStreaming()
            .OnRequestCompleted(response =>
                {
                    // log out the request
                    if (response.RequestBodyInBytes != null)
                    {
                        Console.WriteLine(
                            $"{response.HttpMethod} {response.Uri} \n" +
                            $"{Encoding.UTF8.GetString(response.RequestBodyInBytes)}");
                    }
                    else
                    {
                        Console.WriteLine($"{response.HttpMethod} {response.Uri}");
                    }

                    Console.WriteLine();

                    // log out the response
                    if (response.ResponseBodyInBytes != null)
                    {
                        Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
                                 $"{Encoding.UTF8.GetString(response.ResponseBodyInBytes)}\n" +
                                 $"{new string('-', 30)}\n");
                    }
                    else
                    {
                        Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
                                 $"{new string('-', 30)}\n");
                    }
                });

    var client = new ElasticClient(connectionSettings);

    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    client.CreateIndex(defaultIndex, c => c
        .Mappings(m => m
            .Map<Order>(mm => mm.AutoMap())
            .Map<OrderLine>(mm => mm
                .Parent<Order>()
                .AutoMap()
            )
        )
    );

    var orders = new[]
    {
        new Order { Customer = "Bilbo Baggins" },
        new Order { Customer = "Gandalf the Grey" }
    };

    var orderlines = new Dictionary<string, OrderLine[]>
    {
        { "Bilbo Baggins",
            new []
            {
                new OrderLine { ItemNumber = 1 },
                new OrderLine { ItemNumber = 2 },
                new OrderLine { ItemNumber = 3 },
                new OrderLine { ItemNumber = 4 },
                new OrderLine { ItemNumber = 5 }
            }
        },
        { "Gandalf the Grey",
            new []
            {
                new OrderLine { ItemNumber = 1 },
                new OrderLine { ItemNumber = 2 },
                new OrderLine { ItemNumber = 3 },
                new OrderLine { ItemNumber = 4 }
            }
        }
    };

    client.IndexMany(orders);

    foreach (var lines in orderlines)
    {
        client.Bulk(b => b
            .IndexMany(lines.Value, (bi, d) => bi.Parent(lines.Key))
        );
    }

    client.Refresh(defaultIndex);

    var queryResult = client.Search<Order>(s => s
        .Query(q => +q
            .HasChild<OrderLine>(c => c
                .Query(cq => +cq.MatchAll())
                // min number of child documents that must match
                .MinChildren(5)
            )
        )
    );
}

public class Order
{
    public string Customer { get; set; }
}

public class OrderLine
{
    public int ItemNumber { get; set; }   
}

the query result returns only Bilbo Baggins

Status: 200
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.0,
    "hits" : [ {
      "_index" : "orders",
      "_type" : "order",
      "_id" : "Bilbo Baggins",
      "_score" : 0.0,
      "_source" : {
        "customer" : "Bilbo Baggins"
      }
    } ]
  }
}