NEST:由于搜索为空,弹性搜索性能出现问题

时间:2019-01-17 19:30:29

标签: elasticsearch nest

使用弹性“数字”:“ 6.3.1” “ lucene_version”:“ 7.3.1” 巢:6.1.0

尝试翻译以下搜索。本质上,message1,message2可以具有空字符串值。如果search1Value或search2Value是空字符串,则我不希望在OR条件中存在空字符串的那部分返回任何记录。

这是带有其他条件的大型搜索的一部分...但是,这导致对ES的查询非常慢。创建索引时,除了原始字段外,我还创建了RAW字段,以便能够在NOT EMPTY上进行搜索。我没有尝试过让我正确地进行搜索。是否有其他方法可以做到这一点?如前所述,查询的性能非常糟糕。超过2秒。所涉及的索引大约有60万个文档。逻辑确实起作用。它确实返回正确的文档。

在此先感谢您的帮助!

message1 != "" and message1.StartsWith(search1Value)
OR 
message2 != "" and message2.StartsWith(search2Value)

因此,如果索引中有可用文档的示例...

id, message1, message2
1, "", "abc"
2, "", ""
3, "def", ""
4, "", "ghi"

如果searchValue1是空字符串,而searchValue2是abc,我想返回,只记录1。不记录1、2和4。

为了在这种情况下正确搜索,设置了这样的索引:

public class MessageSearch() {
       public string Message1 {get; set;}
       public string Message2 {get; set;}
    }

public class MessageModelIndex() {
   public string Message1 {get; set;} = ""
   public string Message2 {get; set;} = ""
}


public override CreateIndexDescriptor DefineIndex(string indexName)
        {
            return new CreateIndexDescriptor(indexName).Settings(s => s
                .NumberOfShards(2)                    
                .Mappings(ms => ms
                    .Map<MessageModelIndex>(m => m
                        .Properties(p => p                                
                            .Text(s => s
                                .Name(x => x.Message1)
                                .Fields(ff => ff
                                    .Text(tt => tt
                                        .Name("raw")
                                    )
                                    .Keyword(k => k
                                        .Name("keyword")
                                        .IgnoreAbove(1)
                                    )
                                )
                            )
                            .Text(s => s
                                .Name(x => x.Message2)
                                .Fields(ff => ff
                                    .Text(tt => tt
                                        .Name("raw")
                                    )
                                    .Keyword(k => k
                                        .Name("keyword")
                                        .IgnoreAbove(1)
                                    )
                                )
                            )
                        )
                    ));
        }

以下搜索用于获取这些值:

public void PerformSearch(MessageSearch search) {
                var result = _client.Search<MessageModelIndex>(x => x
               .Index("MessageTest")
               .Size(1000)
               .Query(q => q
                        .Bool(b => b
                                .Must(bm => bm
                                    .Bool(bb => bb
                                        .Should(bbs => bbs
                                            .Bool(bbb => bbb
                                                .Must(mm => mm
                                                    .Bool(bbbb => bbbb
                                                        .MustNot(bbbmn => bbbmn.Term(t => t.Verbatim().Field(f => f.Message1.Suffix("keyword")).Value(string.Empty)))
                                                    ),
                                                    mm => mm
                                                    .Bool(bbbb => bbbb
                                                        .Must(bbbmn => bbbmn.MatchPhrasePrefix(mmp => mmp.Query(search.Message1.Trim()).Field(f => f.Message1.Suffix("raw"))))
                                                    )
                                                 )
                                            ), bbs => bbs
                                            .Bool(bbb => bbb
                                                .Must(mm => mm
                                                    .Bool(bbbb => bbbb
                                                        .MustNot(bbbmn => bbbmn.Term(t => t.Verbatim().Field(f => f.Message2.Suffix("keyword")).Value(string.Empty)))
                                                    ),
                                                    mm => mm
                                                    .Bool(bbbb => bbbb
                                                        .Must(bbbmn => bbbmn.MatchPhrasePrefix(mmp => mmp.Query(search.Message2.Trim()).Field(f => f.Message2.Suffix("raw"))))
                                                    )
                                                 )
                                            )
                                        )
                                    )
                                )
                            )
               )
            );
 }

1 个答案:

答案 0 :(得分:0)

对于您想要的结果,映射和查询看起来不正确。让我们分解一下

  

在创建索引时,除了原始字段外,我还创建RAW字段,以便能够在NOT EMPTY上进行搜索。我没有尝试过让我正确地进行搜索。有其他方法吗?

映射

例如,您拥有的映射

.Text(s => s
    .Name(x => x.Message1)
    .Fields(ff => ff
        .Text(tt => tt
            .Name("raw")
        )
        .Keyword(k => k
            .Name("keyword")
            .IgnoreAbove(1)
        )
    )
)

"raw"字段是多余的,因为它与包含的text数据类型映射相同。

"keyword"多字段将为Message1索引单个或更少的字符串。在这里,如果要使用此多字段来搜索.IgnoreAbove(0)具有空字符串的文档,我想您想使用Message1。但是,我想问一问:能够搜索带有空Message1的文档是否真的有价值?您可以使用exists查询来确定具有值(甚至是空字符串)的文档,如果您确实想搜索包含空消息的文档,则可以使用脚本查询来进行搜索。

最终,我想如果能够在空消息上进行搜索是否很普遍,那么使用这个"keyword"多字段将很有用;我倾向于将其命名为"empty",以便更好地匹配意图。

搜索请求

  

.Index("MessageTest")

索引名称必须小写才能有效。

.Bool(b => b
        .Must(bm => bm
            .Bool(bb => bb
                .Should(bbs => bbs

不需要外部bool查询must子句; should子句可以移出并在外部bool查询中定义。

.Bool(bbb => bbb
    .Must(mm => mm
        .Bool(bbbb => bbbb
            .MustNot(bbbmn => bbbmn.Term(t => t.Verbatim().Field(f => f.Message1.Suffix("keyword")).Value(string.Empty)))
        ),
        mm => mm
        .Bool(bbbb => bbbb
            .Must(bbbmn => bbbmn.MatchPhrasePrefix(mmp => mmp.Query(search.Message1.Trim()).Field(f => f.Message1.Suffix("raw"))))
        )
     )
)

term子句中的must_not查询对我来说似乎是多余的,因为match_phrase_prefix查询的空字符串输入将不匹配任何文档。如果您索引了以下文档,则可以自己查看

var bulkResponse = client.Bulk(b => b
    .IndexMany(new [] 
    {
        new MessageModelIndex { Id = 1, Message1 = "", Message2 = "abc" },
        new MessageModelIndex { Id = 2, Message1 = "", Message2 = "" },
        new MessageModelIndex { Id = 3, Message1 = "def", Message2 = "" },
        new MessageModelIndex { Id = 4, Message1 = "", Message2 = "ghi" },
    })
    .Refresh(Refresh.WaitFor)
);

然后运行搜索

var emptyStringInputResponse = client.Search<MessageModelIndex>(x => x
    .Index(defaultIndex)
    .Query(q => q
        .MatchPhrasePrefix(t => t
            .Verbatim()
            .Field(f => f.Message1)
            .Query("")
        )
    )
);

不返回任何文件。这是因为在索引时对Message1字段进行了分析,并在查询时对了该字段进行了输入。

还请注意,此处需要使用.Verbatim(),因为NEST具有称为 conditionless 查询的概念:如果确定查询为无条件的查询,则它不包含在序列化查询中请求JSON。对于MatchPhrasePrefix查询,如果输入的字符串为空或为空,则查询将变为无条件。使用.Verbatim()会覆盖此无条件行为,从而迫使NEST按原样序列化查询。

查询可以简化为

var searchResponse = client.Search<MessageModelIndex>(x => x
    .Index(defaultIndex)
    .Size(1000)
    .Query(q => q
        .Bool(bb => bb
            .Should(bbs => bbs
                .MatchPhrasePrefix(mmp => mmp
                    .Query(search.Message1.Trim())
                    .Field(f => f.Message1)
                ), bbs => bbs
                .MatchPhrasePrefix(mmp => mmp
                    .Query(search.Message2.Trim())
                    .Field(f => f.Message2)
                )
            )
        )
    )
);

可以用operator overloading on queries进一步简化为

var searchResponse = client.Search<MessageModelIndex>(x => x
    .Index(defaultIndex)
    .Size(1000)
    .Query(q => q
        .MatchPhrasePrefix(mmp => mmp
                .Query(search.Message1.Trim())
                .Field(f => f.Message1)
            ) || q
        .MatchPhrasePrefix(mmp => mmp
            .Query(search.Message2.Trim())
            .Field(f => f.Message2)
        )
    )
);

仅返回id为searchValue1 ""和searchValue2 "abc"的文档。

这是一个完整的示例

private static void Main()
{
    var defaultIndex = "message-test";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

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

    client.CreateIndex(defaultIndex, c => c
        .Mappings(m => m
            .Map<MessageModelIndex>(mm => mm
               .Properties(p => p
                    .Text(s => s
                        .Name(x => x.Message1)
                        .Fields(ff => ff
                            .Keyword(k => k
                                .Name("keyword")
                                .IgnoreAbove(0)
                            )
                        )
                    )
                    .Text(s => s
                        .Name(x => x.Message2)
                        .Fields(ff => ff
                            .Keyword(k => k
                                .Name("keyword")
                                .IgnoreAbove(0)
                            )
                        )
                    )
                )
            )
        )
    );

    var bulkResponse = client.Bulk(b => b
        .IndexMany(new [] 
        {
            new MessageModelIndex { Id = 1, Message1 = "", Message2 = "abc" },
            new MessageModelIndex { Id = 2, Message1 = "", Message2 = "" },
            new MessageModelIndex { Id = 3, Message1 = "def", Message2 = "" },
            new MessageModelIndex { Id = 4, Message1 = "", Message2 = "ghi" },
        })
        .Refresh(Refresh.WaitFor)
    );

    var search = new MessageSearch
    {
        Message1 = "",
        Message2 = "abc"
    };

    var searchResponse = client.Search<MessageModelIndex>(x => x
        .Index(defaultIndex)
        .Size(1000)
        .Query(q => q
            .MatchPhrasePrefix(mmp => mmp
                    .Query(search.Message1.Trim())
                    .Field(f => f.Message1)
                ) || q
            .MatchPhrasePrefix(mmp => mmp
                .Query(search.Message2.Trim())
                .Field(f => f.Message2)
            )
        )
    );
}

public class MessageSearch 
{
    public string Message1 { get; set; }
    public string Message2 { get; set; }
}

public class MessageModelIndex 
{
   public int Id { get; set; }
   public string Message1 { get; set; } = "";
   public string Message2 { get; set; } = "";
}
相关问题