Full text search in mongodb in .net - c#

I have to search contents in all documents in particular collection of mongodb in .net mvc . I have tried with mongodb shell by creating index successfully like here .
db.collection_name.createIndex( { subject: "text" } )
db.collection_name.find( { $text: { $search: "search_word" } } )
It works fine . but when i put it in .net that gives me error . I googled it and got following solution for indexing .
collection.EnsureIndex(new IndexKeysBuilder().Ascending("subject"));
now how can i run this query db.collection_name.find( { $text: { $search: "coffee" } } ) .
I am trying in .net as following way .
collection.CreateIndex("subject":"text");
var query = collection.Find({ $text: { $search: "coffe" }});
but I am getting error on first line "represents text as series of unicode ....syntax error "
2nd line error "There is no argument given that corresponds to required formal parameters " And "unexpected character $ ".
any suggestion will be appreciated .

I could create text indexes with this command:
collection.Indexes.CreateOne(Builders<searchFileByAuthor>.IndexKeys.Text(x=>x.subject));
And than i could query index this way:
collection.Find(Builders<searchFileByAuthor>.Filter.Text("coffe")).ToList();
searchFileByAuthor is just my fake class with subject field:
public class searchFileByAuthor
{
public int Id { get; set; }
public string subject { get; set; }
}

Maksim Simkin answer is correct, althought it is obsolete.
The updated version would be:
collection.Indexes.CreateOne(new CreateIndexModel<YourClass>(Builders<YourClass>.IndexKeys.Text(x => x.something)));
or, if you would like to use the Wildcard Indexing (to index the entire document), you could do like this:
collection.Indexes.CreateOne(new CreateIndexModel<YourClass>(Builders<YourClass>.IndexKeys.Text("$**")));
or maybe you want/have more indexes for some reason, than do this:
var indexWildcardTextSearch = new CreateIndexModel<YourClass>(Builders<YourClass>.IndexKeys.Text("$**"));
List<CreateIndexModel<YourClass>> indexes = new List<CreateIndexModel<YourClass>>();
indexes.Add(indexWildcardTextSearch);
collection.Indexes.CreateMany(indexes);
And to query, it remains the same:
collection.Find(Builders<YourClass>.Filter.Text("something")).ToList();

public List<T> FindSearch<T>(string collectionName, string searchWord) {
IMongoQuery query = Query.Text(searchWord);
List<T> find = getCollection<T>(collectionName).Find(query).ToList();
return find;
}

Related

Azure Search SDK fails partial search when using special characters

I have a below code which should return all the names starting with T&S from azure index
for example the results should be like below
T&S
T&S Limited
T&S Corporation
The search text we see in the code is the UrlEncoded version of "T&S*"
Search Code Block
var response = await _searchClient.Documents.SearchAsync<customDto>("%22T%26S%22*",
new SearchParameters
{
SearchFields = new List<string> { "Name" },
SearchMode = SearchMode.All
});
Custom DTO
public class CustomDto{
public CustomDto(int id,string name)
{
Id=Convert.ToString(id),
Name=name
}
[IsSearchable, IsFilterable]
[System.ComponentModel.DataAnnotations.Key]
public string Id { get; }
[IsSearchable, IsFilterable, IsSortable]
public string Name {get;}
}
Now,
If i put the similar search text on the azure search query window
i get results as expected %22T%26S%22*&searchMode=all&searchFields=Name
But for some reason the code returns empty result. I dont get what am i doing wrong here.
Please assist.
Thank you
Can you try with the following code. This uses Microsoft.Azure.Search SDK (version 10.1.0).
var searchCredentials = new SearchCredentials("<api-key (admin or query>");
var indexClient = new SearchIndexClient("<search-service-name>", "<index-name>", searchCredentials);
var results = indexClient.Documents.Search("\"T\\&S\"*",
new SearchParameters
{
SearchFields = new List<string> { "Name" },
SearchMode = SearchMode.All
});
SDK actually makes a POST request so you don't really have to URL encode the search string (you would need to do that when you issue a GET request). What you need to do is escape & character by prefixing it with a \ and that's what I did. Please see Escaping Special Characters here: https://learn.microsoft.com/en-us/azure/search/query-lucene-syntax#bkmk_syntax for more information.

checking if an item with a valid conditiona actually exist in an foreachloop

I have two Json files . One file (jOne) Specifies to check for the availability of something (some key and value) in the other JSON file (jTwo)
jOne :- Which specifies what are the required values that need to be looked up in the second JSON file
[
{
"IdInOne": "001",
"NameInOne": "Name_2"
},
{
"IdInOne": "002",
"NameInOne": "Name_3"
}
]
jTwo :- File Which is Supposed to have the values specifed in JOne
[
{
"IdInTwo": "001",
"NameInTwo": "Name_1"
},
{
"IdInTwo": "001",
"NameInTwo": "Name_2"
},
{
"IdInTwo": "001",
"NameInTwo": "Name_3"
},
{
"IdInTwo": "002",
"NameInTwo": "Name_4"
}
]
So what I am trying to do is check if a certain specified json key and value pair actually exist in a given separate JSON , so in the Specified example in my first JSON file the value IdInOne": "001",NameInOne": "Name_2" and I want to check if the 2nd JSON file actually has a key and value that matches EXACTLY . In the second JSON There are multiple matches where the IdInOne (001) is equal to the IdInTwo but only a single value has both the ID and Name which matches to a specified one.
I Simply want to validate this and print a message if the match is successful and print a separate value if the match is unsuccesful
Here is the Code that I have tried and I am finding it hard to check if a value actually exists
class Program
{
static void Main(string[] args)
{
string jOne = #"D:\DelTemp\test.json";
string jTwo = #"D:\DelTemp\test2.json";
string jOneContent = File.ReadAllText(jOne);
string jTwoContent = File.ReadAllText(jTwo);
var InfoInOne = JsonConvert.DeserializeObject<List<One>>(jOneContent);
var InfoInTwo = JsonConvert.DeserializeObject<List<Two>>(jTwoContent);
foreach (var itemInOne in InfoInOne)
{
foreach (var itemInTwo in InfoInTwo.Where(n => n.IdInTwo == itemInOne.IdInOne && n.NameInTwo==itemInOne.NameInOne))
{
if (itemInTwo.exists())
{
//sucess message must be printed
}
else
{
Console.WriteLine("Item not found" + itemInTwo.NameInTwo)
}
}
}
}
}
public class One
{
public string IdInOne { get; set; }
public string NameInOne { get; set; }
}
public class Two
{
public string IdInTwo { get; set; }
public string NameInTwo { get; set; }
}
Note :- A downside of my current implementation is bad performance I beileive so is there any better way to implement this . Also only want to print the Error message saying that required/specified object is not found after it has fully iterated and confirmed that it is not there in the second JSON file
Would Really appreciate any help on this
Edit :- The Interesct method does not work as I get the error Two' does not contain a definition for 'exists' and no accessible extension method 'exists' accepting a first argument of type 'Two'
You could use Enumerable.Except. For example,
var dataNotInInfoTwo = InfoInOne.Select(x=>new {ID=x.IdInOne,Name=x.NameInOne})
.Except(InfoInTwo.Select(x=>new {ID=x.IdInTwo,Name=x.NameInTwo}));
foreach(var item in dataNotInInfoTwo)
{
Console.WriteLine($"Data Not Found : ID : {item.ID}, Name:{item.Name}");
}
Output
Data Not Found : ID : 002, Name:Name_3
This is untested for your circumstance but I imagine will work...
Not sure if you want to compare all or any, so here is both:
// Will tell you if all the elements in InfoInOne are also found in InfoInTwo
if (InfoInOne.All(x => InfoInTwo.Contains(x)))
{
}
// Will give you a list of all the items found to be in both lists
var matches = InfoInOne.FindAll(x => InfoInTwo.Contains(x));
EDIT I missed the part where you want separate output for success and no success so for that you can use this:
foreach (var result in InfoInOne)
{
if(InfoInTwo.Contains(result)) //sucess message must be printed
else Console.WriteLine("Item not found" + result.NameInOne) // This in NameInOne, not NameInTwo
}
There should be no need to pick apart the fields here as the objects only contain primitive types, and will be considered equal, or to be contained, in the other list if they match exactly.

Neo4jClient: Merge with OnCreate leads to broken Cypher Query

We are using Neo4j Community 3.2.2 with Neo4jClient 2.0.0.9 and try to create a node if it does not exist. This is covered in the cypher examples and questions like this here on SO, so we thought that should be pretty straight forward:
public class KlientNode
{
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
}
and:
var neuerKlient = new KlientNode { Id = ev.KlientId };
var kq = graphClient.Cypher
.Merge("(klient:Klient { id: {klientId} })")
.OnCreate()
.Set("klient = {neuerKlient}").WithParams(new
{
klientId = neuerKlient.Id,
neuerKlient
});
Console.WriteLine(kq.Query.DebugQueryText);
kq.ExecuteWithoutResults();
So we basically copied the example 1:1.
Unfortunately, this leads to the following output and exception:
MERGE (klient:Klient { id: "80248429-ea80-4a5d-8d4e-88dc1499ea8a" })
ON CREATE
SET klient = {
"id": "80248429-ea80-4a5d-8d4e-88dc1499ea8a"
}
Neo4jClient.NeoException: SyntaxError: Invalid input 'N': expected 'p/P' (line 5, column 2 (offset: 250))
"ON CREATE"
^
The cause seem to be the quotes around the "id" in the SET klient = ... query. If I paste the generated query to the neo4j web console, it shows a syntax error, and if I remove the quotation marks, the query runs just fine.
Anyone has an idea what might be causing the broken query when we just seem to copy the examples almost verbatim?
FWIW, I'm not sure why that happened, but we were able to solve it as follows:
var kq = graphClient.Cypher
.Merge($"(klient:{NodeName} {{ id: {{klientId}} }})")
.OnCreate()
.Set("klient = {neuerKlient}").WithParams(new
{
klientId = ev.KlientId,
neuerKlient = new KlientNode
{
Id = ev.KlientId,
},
});
Console.WriteLine(kq.Query.DebugQueryText);
kq.ExecuteWithoutResults();
The debug output is still the same (ignore the different random Guid):
MERGE (klient:Klient { id: "87798b47-ab1b-49b7-9c5e-018cd244465e" })
ON CREATE
SET klient = {
"id": "87798b47-ab1b-49b7-9c5e-018cd244465e"
}
and the query is still broken if I try to paste it into the neo4j web frontend:
However, the query now executes without an exception.
Since the only change is the shorthand definition of the neuerKlient property in the anonymous object, I assume there is some behaviour internally that was causing the error (even though the debug query output was the same).

LINQ get items in List<AttributeValuePair>

I have a table on my Database where, aside from other columns (one of which is a UniqueIdentifier) I also have one column where I have a JSON array string with values like this (formatted):
[
{
"AttributeId": "fe153d69-8ac1-6e0c-8793-ff0000804eb3",
"AttributeValueId": "64163d69-8ac1-6e0c-8793-ff0000804eb3"
},
{
"AttributeId": "00163d69-8ac1-6e0c-8793-ff0000804eb3",
"AttributeValueId": "67163d69-8ac1-6e0c-8793-ff0000804eb3"
}
]
I then have this AttributeValuePair class which will allow me to read this data on code:
public class AttributeValuePair
{
public AttributeValuePair();
public Guid AttributeId { get; set; }
public Guid AttributeValueId { get; set; }
}
Whenever I get a list of items from this table, I want to be able to filter the resulting array based on only one AttributeValueId and get only the items where this is a match, independently of the value of any other attributes.
Since that on code, to read these attribute collection I must have a List<AttributeValuePair>, how in LINQ can I get the items where a particular AttributeValueId is present?
List<AttributeValuePair> attributeValuePairs = serializer.Deserialize<List<AttributeValuePair>>(item.Variant);
I've been lost at it for two hours already and can't seem to find an escape from this one.
EDIT
Being more clear about the problem, what I'm trying to do is, from a List<ProductVariation>, get the possible values for the attribute "Portions", when the attribute "Days" is the specified value. I'm having a lot of trouble using the serializer to build the LINQ statement.
//This code is wrong, I know, but I'm trying to show what I want
result = model.ProductVariations.Find(x, new {serializer.Deserialize<List<AttributeValuePair>>(item.Variant).Where(valuePair => valuePair.AttributeId == attributeId)});
Can you try
attributeValuePairs.Where(valuePair => valuePair.AttributeId == new Guid("SomeValue"));
The answer to this question was actually a lot simpler than previously expected:
public string SelectedVariation(string mealsAttribute, string portionsAttribute, string product)
{
Guid productId = new Guid(product);
CatalogManager catalogManager = CatalogManager.GetManager();
EcommerceManager ecommerceManager = EcommerceManager.GetManager();
RegisterOrderAccountFormModel model = new RegisterOrderAccountFormModel();
model.Product = catalogManager.GetProduct(productId);
List<ProductVariation> productVariationsCollection = catalogManager.GetProductVariations(productId).ToList();
//This is the really interesting part for the answer:
return productVariationsCollection.Where(x => x.Variant.ToLower().Contains(mealsAttribute.ToLower()) && x.Variant.ToLower().Contains(portionsAttribute.ToLower())).FirstOrDefault().Id.ToString();
}

How to find records based on matching string content in Mongodb using C#

I have a BsonDocument like
{
"Code" : "123ACThe three $#^",
. . . .
}
I want to find the records which is having 3AC anywhere in the string in the field Code
note :- the characers AC should be case insensitive
I am new to mongoDb.How to achieve this ?
Thanks in Advance for replies.
UPDATE
I tried this code db.session.find({ "Code": { "$regex": '^(.?(\b3AC\b)[^$])$' } } );
Got this working
db.session.find({ "Code": { "$regex": '3AC.*'} } );
Is this the only way ? and how to achieve in C# using native driver ?
This should do the trick:
db.session.find({Code: /3AC/ })
or if you want to ignore case:
db.session.find({Code: /3AC/i })
While doing a search like that is not generally recommended as an index cannot be used (as your expression is not anchored to the start of the string), in C# you can use the Query classes' Matches method:
Query.Matches("Code", "3AC.*")
where "3AC.*" represents the regular expression string you're using.
MongoCollection<ExampleType> exampleCollection;
var query = Query.Matches("Code", "3AC.*");
foreach (var example in exampleCollection.Find(query)) {
//
}
You can also use the driver's LINQ interface and the driver will build the regular expression for you:
var query = sessionCollection.AsQueryable<Session>()
.Where(s => s.Code.Contains("3AC"));
foreach (var session in query) { ... }

Categories