使用弹性“数字”:“ 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"))))
)
)
)
)
)
)
)
)
);
}
答案 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; } = "";
}