Отображение и запрос выглядят некорректно для желаемого результата. Давайте разберемся с этим
Я создаю поля RAW в дополнение к исходным полям при создании индекса, чтобы иметь возможность поиска по НЕ ПУСТО. Ничто другое, что я пробовал, не позволяло мне делать этот поиск правильно Есть ли другой способ сделать это?
Отображение
В качестве примера, у вас есть отображение
.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
query 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 имеет концепцию, известную как безусловные запросы: если запрос определен как безусловный, он не включается в сериализованный запрос 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)
)
)
)
)
);
, что можно еще больше упростить с помощью перегрузки оператора на запросы до
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)
)
)
);
, который возвращает только документ с идентификатором 1 для 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; } = "";
}