How to apply exact instead match by Nest api? - elasticsearch

How can I get AEntity where BEntityProp equals "Bprop 3"?
internal class Program
{
public class AEntity
{
public Guid Id { get; set; }
public BEntity BEntityProp { get; set; }
}
public class BEntity
{
public Guid Id { get; set; }
public string Bprop { get; set; }
}
private static void Main(string[] args)
{
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node, defaultIndex: "default_index");
var client = new ElasticClient(settings);
var entities = new List<AEntity>(100);
for (var i = 0; i < 100; i++)
client.Index(new AEntity
{
Id = Guid.NewGuid(),
BEntityProp =
new BEntity
{
Id = Guid.NewGuid(),
Bprop = "Bprop " + i.ToString(CultureInfo.InvariantCulture)
}
});
Thread.Sleep(1000);
var searchDescriptor = new SearchDescriptor<AEntity>();
//how to apply exact instead match by Nest api?
List<AEntity> expected = client.Search<AEntity>(
searchDescriptor.Query(qd => qd.Match(
mqd => mqd.OnField(x => x.BEntityProp.Bprop).Query("bprop 3")))).Documents.ToList();
try
{
Assert.IsTrue(expected.Count == 1);
}
finally
{
client.DeleteIndex(di => di.Indices("default_index", "default_index" + "*"));
}
}
}
As a result I want to have 1 AEntity with Bprop equals "Bprop 3" but I have all matches "Bprop".
The request looks like:
expected :
{
"query": {
"match": { <-- how to apply exact instead match by Nest api?
"bEntityProp.bprop": {
"query": "bprop 3"
}
}
}
}

I found the ansvwer :
List<AEntity> expected = client.Search<AEntity>(
searchDescriptor.Query(qd => qd.MatchPhrase(
mqd => mqd.OnField(x => x.BEntityProp.Bprop).Query("bprop 3")))).Documents.ToList();

If you want the exact match then use a Term query:
List<AEntity> expected = client.Search<AEntity>(
searchDescriptor.Query(qd => qd.Term(
mqd => mqd.OnField(x => x.BEntityProp.Bprop).Value("bprop 3"))))
.Documents.ToList();
More on Term vs Match can be found in this discussion.

Related

Intersection of arrays in LINQ to CosmosDB

I'm trying find all items in my database that have at least one value in an array that matches any value in an array that I have in my code (the intersection of the two arrays should not be empty).
Basically, I'm trying to achieve this :
public List<Book> ListBooks(string partitionKey, List<string> categories)
{
return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
{
PartitionKey = new PartitionKey(partitionKey)
})
.Where(b => b.Categories.Any(c => categories.Contains(c))
.ToList();
}
With the Book class looking like this :
public class Book
{
public string id {get;set;}
public string Title {get;set;}
public string AuthorName {get;set;}
public List<string> Categories {get;set;}
}
However the SDK throws an exception saying that Method 'Any' is not supported when executing this code.
This doesn't work either :
return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
{
PartitionKey = new PartitionKey(partitionKey)
})
.Where(b => categories.Any(c => b.Categories.Contains(c))
.ToList();
The following code works because there's only one category to find :
public List<Book> ListBooksAsync(string category)
{
return _client.CreateDocumentQuery<Book>(GetCollectionUri())
.Where(b => b.Categories.Contains(category))
.ToList();
}
In plain SQL, I can queue multiple ARRAY_CONTAINS with several OR the query executes correctly.
SELECT * FROM root
WHERE ARRAY_CONTAINS(root["Categories"], 'Humor')
OR ARRAY_CONTAINS(root["Categories"], 'Fantasy')
OR ARRAY_CONTAINS(root["Categories"], 'Legend')
I'm trying to find the best way to achieve this with LINQ, but I'm not even sure it's possible.
In this situation I've used a helper method to combine expressions in a way that evaluates to SQL like in your final example. The helper method 'MakeOrExpression' below lets you pass a number of predicates (in your case the individual checks for b.Categories.Contains(category)) and produces a single expression you can put in the argument to .Where(expression) on your document query.
class Program
{
private class Book
{
public string id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public List<string> Categories { get; set; }
}
static void Main(string[] args)
{
var comparison = new[] { "a", "b", "c" };
var target = new Book[] {
new Book { id = "book1", Categories = new List<string> { "b", "z" } },
new Book { id = "book2", Categories = new List<string> { "s", "t" } },
new Book { id = "book3", Categories = new List<string> { "z", "a" } } };
var results = target.AsQueryable()
.Where(MakeOrExpression(comparison.Select(x => (Expression<Func<Book, bool>>)(y => y.Categories.Contains(x))).ToArray()));
foreach (var result in results)
{
// Should be book1 and book3
Console.WriteLine(result.id);
}
Console.ReadLine();
}
private static Expression<Func<T,bool>> MakeOrExpression<T>(params Expression<Func<T,bool>>[] inputExpressions)
{
var combinedExpression = inputExpressions.Skip(1).Aggregate(
inputExpressions[0].Body,
(agg, x) => Expression.OrElse(agg, x.Body));
var parameterExpression = Expression.Parameter(typeof(T));
var replaceParameterVisitor = new ReplaceParameterVisitor(parameterExpression,
Enumerable.SelectMany(inputExpressions, ((Expression<Func<T, bool>> x) => x.Parameters)));
var mergedExpression = replaceParameterVisitor.Visit(combinedExpression);
var result = Expression.Lambda<Func<T, bool>>(mergedExpression, parameterExpression);
return result;
}
private class ReplaceParameterVisitor : ExpressionVisitor
{
private readonly IEnumerable<ParameterExpression> targetParameterExpressions;
private readonly ParameterExpression parameterExpression;
public ReplaceParameterVisitor(ParameterExpression parameterExpressionParam, IEnumerable<ParameterExpression> targetParameterExpressionsParam)
{
this.parameterExpression = parameterExpressionParam;
this.targetParameterExpressions = targetParameterExpressionsParam;
}
public override Expression Visit(Expression node)
=> targetParameterExpressions.Contains(node) ? this.parameterExpression : base.Visit(node);
}
}

C# Mongo db driver updating an existing document

I have an existing document in mongo db with certain properties. When i am trying to update this document with a document with the same id but having more properties it is not updating it. I am using mongo db driver version 2.4.4
Following is the existing document in my db
{
"_id" : "1234",
"AccountNumber" : "3453535345354",
"Name" : "new1",
"PhoneNumber" : "34534535353534543",
"ETag" : "6ba32e6e-3808-41f5-9b28-0ea882d9c629",
"Id" : "1234"
}
Now when i am trying to update/ replace this document with the following document it does not work:
{
"AccountNumber": "3453535345354",
"Name":"new1",
"PhoneNumber":"34534535353534543",
"TempProperty":"something new",
"ETag" : "6ba32e6e-3808-41f5-9b28-0ea882d9c629",
"Id" : "1234",
"_id" : "1234"
}
Following is the C# code for my upsert method:
public async Task UpsertDocument(string collectionId, string documentId, BsonDocument item)
{
var collection = database.GetCollection<BsonDocument>(collectionId);
var initialCorrelationId = item["ETag"].AsString;
item["ETag"] = Guid.NewGuid().ToString();
var builders = Builders<BsonDocument>.Filter;
var filter = builders.Eq(x => x["Id"], documentId) & builders.Eq(x => x["ETag"], initialCorrelationId);
var options = new FindOneAndReplaceOptions<BsonDocument, BsonDocument>
{
ReturnDocument = ReturnDocument.Before,
IsUpsert = true
};
try
{
item["Id"] = documentId;
item["_id"] = documentId;
await collection.FindOneAndReplaceAsync(filter, item, options);
}
catch (MongoCommandException ex) when (ex.Code == 11000)
{
throw new ConcurrencyException(
$"Error upserting item with id {documentId} and etag {item["Etag"]}{Environment.NewLine}{item.ToJson()}");
}
}
Any help would be much appreciated.
Assuming you have a class as follows:
public class Thing
{
public ObjectId Id { get; set; }
public string AccountNumber { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string ETag { get; set; }
public string SecondId { get; set; }
}
The following code does what you want:
var context = new Context();
var tempProperty = "newValue";
var id = "1234"; // This should be unique
var builder = Builders<Thing>.Filter;
var filter = builder.Eq(x => x.SecondId, id);
var update = Builders<Thing>.Update
.Set("TempProperty", tempProperty);
context.ThingCollection.UpdateOne(filter, update, new UpdateOptions() {IsUpsert = true});
I just renamed Id to SecondId.

Linq Split properties of Class and assign it to another Custom Class

I have a Complex Situation now and i am terribly stuck. Kindly Let me know if you can share some light to it.
I have a
List Which will have the Following properties
public class Categories
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
}
This will have Values like
Category1/SubCategory1
cat1/sc1
5
Category1/SubCategory2
cat1/sc2
4
Category 2/Subcategory1
cat2/sc1
5
Category 2/Subcategory2
cat2/sc2
23
I created a Custom Class to fill in the values
public class JobCateogry
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
public List<JobCateogry> SubCategories { get; set; }
}
I have to Split the String in the Code Value and assign it to the SubCategory.
Like My Final out of jobCategory would be
Category1
Cat1
9
SubCategory1
sub1
5
SubCateogry2
sub2
4
I tried to Split the string and assign it to the new class in two step first by splitting and then by assiging. But i am sure i am doing it the wrong way, because the moment i split, i loose the count .
var lstCategory = Categories
.Where(i => i.count > 0)
.Select(item => item.valueCode.Split('/')
.Select(k =>(k)).ToList();
List<JobCategories> jobcategories = lstCategory
.Select(item => item.Split(QueryStringConstants.CAT_SEPERATOR.ToCharArray()[0]))
.GroupBy(tokens => tokens[0].Trim(), tokens => tokens[1])
.Select(g => new JobCategories(g.Key, g.DisplayName,g.ToList(),)).ToList();
Can you please help?
A bit weird task
It might not be the best solution and it only works with the two layers :-), and i tried keeping a lot of linq for the fun of it
anyway hope it can get you moving forward.
full code snippet https://gist.github.com/cbpetersen/db698def9a04ebb2abbc
static void Main(string[] args)
{
var cats = new[]
{
new Categories { Count = "5", ValueCode = "cat1/sc1", DisplayName = "Category1/SubCategory1" },
new Categories { Count = "4", ValueCode = "cat1/sc2", DisplayName = "Category1/SubCategory2" },
new Categories { Count = "5", ValueCode = "cat2/sc1", DisplayName = "Category2/Subcategory1" },
new Categories { Count = "23", ValueCode = "cat2/sc2", DisplayName = "Category2/Subcategory2" }
};
var categories = cats.Select(x => x.DisplayName.Split('/')[0]).Distinct();
var list = new List<JobCateogries>();
foreach (var category in categories)
{
var a = new JobCateogries
{
ValueCode = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => x.ValueCode.Split('/')[0]).FirstOrDefault(),
DisplayName = category,
SubCategories = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => new JobCateogries
{
SubCategories = new List<JobCateogries>(),
Count = x.Count,
DisplayName = x.DisplayName.Split('/')[1],
ValueCode = x.ValueCode.Split('/')[1]
}).ToList(),
};
a.Count = a.SubCategories.Select(x => int.Parse(x.Count)).Sum().ToString();
list.Add(a);
}
list.ForEach(x => Print(x));
Console.ReadKey();
}
public static void Print(JobCateogries category, int indent = 0)
{
var prefix = string.Empty.PadLeft(indent);
Console.WriteLine(prefix + category.DisplayName);
Console.WriteLine(prefix + category.ValueCode);
Console.WriteLine(prefix + category.Count);
category.SubCategories.ForEach(x => Print(x, indent + 4));
}

Generate list from treeitem using c#

we have a list of the class
public class DummyClass
{
public string Text { get; set; }
public int LevelNo { get; set; }
public List<DummyClass> Children { get; set; }
}
we want to add this list to another list with class.
public class FragmentLevel
{
public int currentLevelNo { get; set; }
public int ParentLevelNo { get; set; }
public string Text { get; set; }
}
we need the result like
var list = new List<FragmentLevel>
{
new FragmentLevel{ id = 1, text = "Root" },
new FragmentLevel{ id = 2, parent= 1, text = "Node-1.1" },
new FragmentLevel{ id = 3, parent= 2, text = "Node-1.1.1" }
};
For getting result we are doing like
for (int i = 0; i < DummyClassList.Count; i++)
{
list.Add(new FragmentLevel
{
currentLevelNo = DummyClassList[i].LevelNo,
Text = DummyClassList[i].Text,
});
do
{
for (int j = 0; j < DummyClassList[i].Children.Count; j++)
{
list1.Add(new FragmentLevel
{
LevelNo = DummyClassList[i].Children[j].LevelNo,
Text = DummyClassList[i].Children[j].Text,
});
}
} while (DummyClassList[i].Children[i].Children != null);
}
But this will give wrong result. How we can get the result?
Try this way of filling the fragments recursively,
private static void FillFragments(List<DummyClass> DummyClassList, List<FragmentLevel> list)
{
for (int i = 0; i < DummyClassList.Count; i++)
{
list.Add(new FragmentLevel
{
currentLevelNo = DummyClassList[i].LevelNo,
Text = DummyClassList[i].Text,
});
if (DummyClassList[i].Children != null && DummyClassList[i].Children.Count > 0)
{
FillFragments(DummyClassList[i].Children, list);
}
}
}
and the main method would look like this
static void Main(string[] args)
{
var DummyClassList = new List<DummyClass>
{
new DummyClass
{
Children = new List<DummyClass>
{
new DummyClass{
Children = null,
LevelNo=2,
Text = "two"
}
},
LevelNo = 1,
Text = "one"
}
};
var list = new List<FragmentLevel>();
FillFragments(DummyClassList, list);
}

Help with linq query. many to many

I have a collection(people) that has a many-to-many reference to another collection(dogs). Suspend your disbelief on how there can be more than one people per dog. People just contains member which is an List<Dog>
I would like to select all the people where the people have a certain property(specified in an IList) and pets have a certain property(specified in an IList).
E.g. I have an IList (used for this query only) with the dog’s property value.
public enum EnumLikesToBite
{
No,
Yes,
Sometimes
}
IList <<EnumLikesToBite>> listDogsMayBite =
{ { EnumLikesToBite.Yes},
{ EnumLikesToBite.Sometimes}};
Then another list for the peoples property:
public enum EnumKeepsPetWith
{
Chain,
String,
Rubberband
}
IList <EnumKeepsPetWith> listPeopleWhoDontRestrainDog =
{ { EnumKeepsPetWith.String },
{ EnumKeepsPetWith.Rubberband}};
How can I query out all the people who have a dog that may bite and don’t restrain dog.
Like this pseudo code:
Var result = from p in People where p.KeepsPet in listPeopleWhoDontRestrainDog and dog.LikesToBite in listDogsMayBite.
Result has all the people. Of course if I could get all the dogs who may bite under those people that would be great.
List<int> mayBite = new List<int>()
{
(int) EnumLikesToBite.Yes,
(int) EnumLikesToBite.Maybe
}
List<int> poorRestraint = new List<int>()
{
(int) EnumKeepsPetWith.String,
(int) EnumKeepsPetWith.RubberBand
}
IQueryable<Person> query =
from p in db.People
where poorRestraint.Contains(p.KeepsPetWith)
where p.DogPeople.Any(dp => mayBite.Contains(dp.Dog.DoesBite))
select p;
var query =
from p in db.People
where poorRestraint.Contains(p.KeepsPetWith)
let bitingDogs =
from dp in p.DogPeople
let d = dp.Dog
where mayBite.Contains(d.DoesBite)
where bitingDogs.Any()
select new {Person = p, BitingDogs = bitingDogs.ToList()};
Maybe this code will help.. One of the possible solution are:
var result =
peoples.Where(y => dontRestrainDog.Contains(y.KeepsPetWith) && y.Dogs.Any(x => dogsMayBite.Contains(x.LikesToBite))).ToList();
result.ForEach(y => y.Dogs = y.Dogs.Where(x => dogsMayBite.Contains(x.LikesToBite)).ToList());
which you can see an example of here:
class Program
{
static void Main(string[] args)
{
IList<EnumLikesToBite> dogsMayBite = new List<EnumLikesToBite>
{
{ EnumLikesToBite.Yes }, { EnumLikesToBite.Sometimes }
};
IList<EnumKeepsPetWith> dontRestrainDog = new List<EnumKeepsPetWith>
{
{ EnumKeepsPetWith.String }, { EnumKeepsPetWith.Rubberband }
};
var peoples = new List<People>();
var dogs = new List<Dog>();
Random gen = new Random(2);
for(int i = 0; i < 10; i++)
{
People p = new People
{
PeopleId = i,
KeepsPetWith = (EnumKeepsPetWith) (gen.Next(10)%3),
Dogs = new List<Dog>()
};
Dog d = new Dog
{
DogId = i,
LikesToBite = (EnumLikesToBite) (gen.Next(10)%3),
Peoples = new List<People>()
};
peoples.Add(p);
dogs.Add(d);
}
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
if (gen.Next(10)>7)
{
peoples[i].Dogs.Add(dogs[j]);
}
if (gen.Next(10)>7)
{
dogs[i].Peoples.Add(peoples[j]);
}
}
}
PrintDogs(dogs);
PrintPeoples(peoples);
var result =
peoples.Where(y => dontRestrainDog.Contains(y.KeepsPetWith) && y.Dogs.Any(x => dogsMayBite.Contains(x.LikesToBite))).ToList();
result.ForEach(y => y.Dogs = y.Dogs.Where(x => dogsMayBite.Contains(x.LikesToBite)).ToList());
Console.WriteLine("===================");
PrintPeoples(result);
Console.ReadLine();
}
private static void PrintPeoples(List<People> peoples)
{
Console.WriteLine("=Peoples=");
foreach (var people in peoples)
{
Console.WriteLine("Id: {0}", people.PeopleId);
Console.WriteLine("KeepsPetWith: {0}", people.KeepsPetWith);
Console.WriteLine("Dogs: ");
foreach (var dog in people.Dogs)
{
Console.Write("{0}, ", dog.DogId);
}
Console.WriteLine();
}
}
private static void PrintDogs(List<Dog> dogs)
{
Console.WriteLine("=Dogs=");
foreach (var dog in dogs)
{
Console.WriteLine("Id: {0}", dog.DogId);
Console.WriteLine("LikesToBite: {0}", dog.LikesToBite);
Console.WriteLine("Peoples: ");
foreach (var people in dog.Peoples)
{
Console.Write("{0}, ", people.PeopleId);
}
Console.WriteLine();
}
}
}
public class People
{
public int PeopleId { get; set; }
public EnumKeepsPetWith KeepsPetWith { get; set; }
public IList<Dog> Dogs { get; set; }
}
public class Dog
{
public int DogId { get; set; }
public EnumLikesToBite LikesToBite { get; set; }
public IList<People> Peoples { get; set; }
}
public enum EnumLikesToBite
{
No,
Yes,
Sometimes
}
public enum EnumKeepsPetWith
{
Chain,
String,
Rubberband
}

Resources