Amir Sasson
Amir Sasson

Reputation: 11543

Elasticsearch Aggregation on objects by query on other documents

Lets say i have an index that contains documents that represent a Message in a discussion.
that document owns a discussionId property. (it also has its own ID "that represent MessageId")

now, i need to find all discussionIds that have no documents (messages) that match a query.
for example:
"Find all discussionIds , that have no message that contains the text 'YO YO'"

how can i do that?

the class is similar to this:

  public class Message
  {
     public string Id{get;set}
     public string DiscussionId {get;set}
     public string Text{get;set}
  }

Upvotes: 0

Views: 56

Answers (1)

Russ Cam
Russ Cam

Reputation: 125538

You just need to wrap the query that would find matches for the phrase "YO YO" in a bool query must_not clause.

With NEST

client.Search<Message>(s => s
    .Query(q => q
        .Bool(b => b
            .MustNot(mn => mn
                .MatchPhrase(m => m
                    .Field(f => f.Text)
                    .Query("YO YO")
                )
            )
        )
    )
);

which, with operator overloading, can be shortened to

client.Search<Message>(s => s
    .Query(q => !q
        .MatchPhrase(m => m
            .Field(f => f.Text)
            .Query("YO YO")
        )
    )
);

Both produce the query

{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "text": {
              "type": "phrase",
              "query": "YO YO"
            }
          }
        }
      ]
    }
  }
}

To only return DiscussionId values, you can use source filtering

client.Search<Message>(s => s
    .Source(sf => sf
        .Includes(f => f
            .Field(ff => ff.DiscussionId)
        )
    )
    .Query(q => !q
        .MatchPhrase(m => m
            .Field(f => f.Text)
            .Query("YO YO")
        )
    )
);

And, if you want to get them all, you can use the scroll API

var searchResponse = client.Search<Message>(s => s
    .Scroll("1m")
    .Source(sf => sf
        .Includes(f => f
            .Field(ff => ff.DiscussionId)
        )
    )
    .Query(q => !q
        .MatchPhrase(m => m
            .Field(f => f.Text)
            .Query("YO YO")
        )
    )
);

// fetch the next batch of documents, using the scroll id returned from
// the previous call. Do this in a loop until no more docs are returned.
searchResponse = client.Scroll<Message>("1m", searchResponse.ScrollId);

Upvotes: 1

Related Questions