MongoDB .NET driver and text searchMongoDB Full-TextSearch C# DriverHow to query MongoDB with “like”?How to specify range criterial for field in MongoDb for C# driverHow do I drop a MongoDB database from the command line?Mongodb c# driver FindAll with setFields and AsQueryablecomposite query to find documents in MongoDb C# driverHow to create MongoDB MultiKey index on attribute of items in an array .NET DriverMongoDB Full and Partial Text SearchVisualizing MongoDB C# Linq query for Driver version 2.4MongoDB text search using c# Driver 2.4.4MongoDB text search using mongo-go-driver
Smoothness of finite-dimensional functional calculus
Can I ask the recruiters in my resume to put the reason why I am rejected?
Why can't I see bouncing of a switch on an oscilloscope?
How did the USSR manage to innovate in an environment characterized by government censorship and high bureaucracy?
Why doesn't Newton's third law mean a person bounces back to where they started when they hit the ground?
Why Is Death Allowed In the Matrix?
How old can references or sources in a thesis be?
Is it important to consider tone, melody, and musical form while writing a song?
Approximately how much travel time was saved by the opening of the Suez Canal in 1869?
What is the word for reserving something for yourself before others do?
How much RAM could one put in a typical 80386 setup?
Why does Kotter return in Welcome Back Kotter?
What would happen to a modern skyscraper if it rains micro blackholes?
What's the point of deactivating Num Lock on login screens?
Either or Neither in sentence with another negative
Mathematical cryptic clues
Is it possible to do 50 km distance without any previous training?
How is it possible to have an ability score that is less than 3?
Collect Fourier series terms
Why are 150k or 200k jobs considered good when there are 300k+ births a month?
How to add double frame in tcolorbox?
can i play a electric guitar through a bass amp?
Have astronauts in space suits ever taken selfies? If so, how?
What do the dots in this tr command do: tr .............A-Z A-ZA-Z <<< "JVPQBOV" (with 13 dots)
MongoDB .NET driver and text search
MongoDB Full-TextSearch C# DriverHow to query MongoDB with “like”?How to specify range criterial for field in MongoDb for C# driverHow do I drop a MongoDB database from the command line?Mongodb c# driver FindAll with setFields and AsQueryablecomposite query to find documents in MongoDb C# driverHow to create MongoDB MultiKey index on attribute of items in an array .NET DriverMongoDB Full and Partial Text SearchVisualizing MongoDB C# Linq query for Driver version 2.4MongoDB text search using c# Driver 2.4.4MongoDB text search using mongo-go-driver
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I am using this MongoDB driver: https://mongodb.github.io/mongo-csharp-driver/
and I would like to search using a text index, which (I think) is created on all text fields like so:
"_fts" : "text",
"_ftsx" : 1
I am using linq queries to filter the data, example:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
IMongoQueryable<MyTypeSerializable> queryable = aCollection.AsQueryable()
.Where(e=> e.Field == 1);
var result = queryable.ToList();
How do I utilize the text search using this method?
mongodb linq
add a comment |
I am using this MongoDB driver: https://mongodb.github.io/mongo-csharp-driver/
and I would like to search using a text index, which (I think) is created on all text fields like so:
"_fts" : "text",
"_ftsx" : 1
I am using linq queries to filter the data, example:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
IMongoQueryable<MyTypeSerializable> queryable = aCollection.AsQueryable()
.Where(e=> e.Field == 1);
var result = queryable.ToList();
How do I utilize the text search using this method?
mongodb linq
add a comment |
I am using this MongoDB driver: https://mongodb.github.io/mongo-csharp-driver/
and I would like to search using a text index, which (I think) is created on all text fields like so:
"_fts" : "text",
"_ftsx" : 1
I am using linq queries to filter the data, example:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
IMongoQueryable<MyTypeSerializable> queryable = aCollection.AsQueryable()
.Where(e=> e.Field == 1);
var result = queryable.ToList();
How do I utilize the text search using this method?
mongodb linq
I am using this MongoDB driver: https://mongodb.github.io/mongo-csharp-driver/
and I would like to search using a text index, which (I think) is created on all text fields like so:
"_fts" : "text",
"_ftsx" : 1
I am using linq queries to filter the data, example:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
IMongoQueryable<MyTypeSerializable> queryable = aCollection.AsQueryable()
.Where(e=> e.Field == 1);
var result = queryable.ToList();
How do I utilize the text search using this method?
mongodb linq
mongodb linq
asked Dec 1 '16 at 16:03
user1713059user1713059
3721618
3721618
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
Looking at the PredicateTranslator within the C# MongoDB driver there isn't any expression that gets converted in to a text query. So you won't be able to achive a text query using a linq query.
- PredicateTranslator.cs
- PredicateTranslatorTests.cs
However you could try just doing a text search with the Builder<>:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
var cursor = await aCollection.FindAsync(Builders<MyTypeSerializable>.Filter.Text("search"));
var results = await cursor.ToListAsync();
Details about the text filter is here https://docs.mongodb.com/manual/reference/operator/query/text/
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I docursor.ToEnumerable().Where(e => e.Field == 1)and it will actually get "materialized" after a.ToList()at the end?
– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );
– Kevin Smith
Dec 7 '16 at 14:15
add a comment |
It's possible to modify MongoDb driver source code. Let me explain it to you:
- Consider "PredicateTranslator" does not convert linq Expression into "$text" query. However there is a Text() method "FilterDefinitionBuilder" class, "PredicateTranslator" does not know the entity class property has a text search index.
- You have to mark entity class property (a condition in predicate statement) with an Attribute. The attribute works to remark property has a full text search index.
- From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"
Let me show you some code:
At MongoDB.Bson project create an Attribute as shown below:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonFullTextSearchAttribute : AttributeAt your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:
public class History
[MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
public string ObjectJSON get; set;At MongoDB.Driver.Linq.Translators.QueryableTranslator.cs
Add a field that keeps entity class type in Expression>, as shown below:
private Type _sourceObjectTypeInExpression;Add a method to get entity class type as shown below:
private void GetObjectType(Expression node)
if (node.Type != null && node.Type.GenericTypeArguments != null && node.Type.GenericTypeArguments.Length > 0)
this._sourceObjectTypeInExpression = node.Type.GenericTypeArguments[0];
Replace "public static QueryableTranslation Translate()" method as shown below:
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
BindingFlags.NonPublic)
.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorParameterTypes))
.Single();
var constructorParameters = new object[] translator._stages, translator._outputSerializer ;
var model = (QueryableExecutionModel)constructorInfo.Invoke(constructorParameters);
return new QueryableTranslation(model, translator._resultTransformer);In TranslateWhere() method pass "_sourceObjectTypeInExpression" field to PredicateTranslator.Translate() static method
var predicateValue = PredicateTranslator.Translate(node.Predicate, _serializerRegistry, this._sourceObjectTypeInExpression);B. MongoDB.Driver.Linq.Translators.PredicateTranslator.cs
- Add a field: "private Type sourceObjectTypeInExpression = null;"- Replace constructor as shown below (there has to be only one constructor);
private PredicateTranslator(Type _sourceObjectTypeInExpression)
this.sourceObjectTypeInExpression = _sourceObjectTypeInExpression;
- Replace function "public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry)" as shown below;
public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry, Type sourceObjectTypeInExpression)
var translator = new PredicateTranslator(sourceObjectTypeInExpression);
node = FieldExpressionFlattener.FlattenFields(node);
return translator.Translate(node)
.Render(serializerRegistry.GetSerializer<BsonDocument>(), serializerRegistry);
- Add these lines for reflection cache:
#region FullTextSearch
private static readonly object mSysncFullTextSearchObjectCache = new object();
private static ConcurrentDictionary<string, List<string>> _fullTextSearchObjectCache = null;
private static ConcurrentDictionary<string, List<string>> FullTextSearchObjectCache
get
if (_fullTextSearchObjectCache == null)
lock (mSysncFullTextSearchObjectCache)
try
if (_fullTextSearchObjectCache == null)
_fullTextSearchObjectCache = new ConcurrentDictionary<string, List<string>>();
finally
Monitor.PulseAll(mSysncFullTextSearchObjectCache);
return _fullTextSearchObjectCache;
private bool IsFullTextSearchProp(Type entityType, string propName)
bool retVal = false;
string entityName = entityType.Name;
this.SetObject2FullTextSearchObjectCache(entityType);
if (FullTextSearchObjectCache.ContainsKey(entityName))
List<string> x = FullTextSearchObjectCache[entityName];
retVal = x.Any(p => p == propName);
return retVal;
private void SetObject2FullTextSearchObjectCache(Type entityType)
string entityName = entityType.Name;
if (!FullTextSearchObjectCache.ContainsKey(entityName))
BindingFlags.Instance);
foreach (FieldInfo tmp in currentFields)
var attributes = tmp.GetCustomAttributes();
BsonFullTextSearchAttribute x = (BsonFullTextSearchAttribute)attributes.FirstOrDefault(a => typeof(BsonFullTextSearchAttribute) == a.GetType());
if (x != null)
retVal.Add(tmp.Name);
FullTextSearchObjectCache.AddOrUpdate(entityName, retVal, (k, v) => v);
#endregion
- Replace "switch (operatorType)" switch in "private FilterDefinition<BsonDocument> TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, fieldExpression.FieldName);
switch (operatorType)
case ExpressionType.Equal:
if (!isFullTextSearchProp)
return __builder.Eq(fieldExpression.FieldName, serializedValue);
else
return __builder.Text(serializedValue.ToString());
case ExpressionType.GreaterThan: return __builder.Gt(fieldExpression.FieldName, serializedValue);
case ExpressionType.GreaterThanOrEqual: return __builder.Gte(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThan: return __builder.Lt(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThanOrEqual: return __builder.Lte(fieldExpression.FieldName, serializedValue);
case ExpressionType.NotEqual:
if (!isFullTextSearchProp)
return __builder.Ne(fieldExpression.FieldName, serializedValue);
else
throw new ApplicationException(string.Format("Cannot use "NotEqual" on FullTextSearch property: "0"", fieldExpression.FieldName));
- Replace "switch (methodCallExpression.Method.Name)" switch in "private FilterDefinition<BsonDocument> TranslateStringQuery(MethodCallExpression methodCallExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, tmpFieldExpression.FieldName);
var pattern = Regex.Escape((string)constantExpression.Value);
if (!isFullTextSearchProp)
switch (methodCallExpression.Method.Name)
case "Contains": pattern = ".*" + pattern + ".*"; break;
case "EndsWith": pattern = ".*" + pattern; break;
case "StartsWith": pattern = pattern + ".*"; break; // query optimizer will use index for rooted regular expressions
default: return null;
var caseInsensitive = false;
MethodCallExpression stringMethodCallExpression;
while ((stringMethodCallExpression = stringExpression as MethodCallExpression) != null)
trimEnd)
var trimCharsPattern = GetTrimCharsPattern(trimCharsExpression);
if (trimCharsPattern == null)
return null;
if (trimStart)
pattern = trimCharsPattern + pattern;
if (trimEnd)
pattern = pattern + trimCharsPattern;
stringExpression = stringMethodCallExpression.Object;
pattern = "^" + pattern + "$";
if (pattern.StartsWith("^.*"))
pattern = pattern.Substring(3);
if (pattern.EndsWith(".*$"))
pattern = pattern.Substring(0, pattern.Length - 3);
var fieldExpression = GetFieldExpression(stringExpression);
var options = caseInsensitive ? "is" : "s";
return __builder.Regex(fieldExpression.FieldName, new BsonRegularExpression(pattern, options));
else
return __builder.Text(pattern);
add a comment |
Searching for solution I found FilterDefinition<T>.Inject() extension method.
So we can go deeper and create one more extension on IMongoQueryable<T>:
public static class MongoQueryableFullTextExtensions
public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search)
var filter = Builders<T>.Filter.Text(search);
return query.Where(_ => filter.Inject());
And use it like this:
IMongoDatabase database = GetMyDatabase();
var results = database
.GetCollection<Blog>("Blogs")
.AsQueryable()
.WhereText("stackoverflow")
.Take(10)
.ToArray();
Hope this helps somebody :)
add a comment |
How about:
IMongoQueryable<MyTypeSerializable> queryable = aCollection
.AsQueryable()
.Where(e=> e.Field.Contains("term"));
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40915209%2fmongodb-net-driver-and-text-search%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Looking at the PredicateTranslator within the C# MongoDB driver there isn't any expression that gets converted in to a text query. So you won't be able to achive a text query using a linq query.
- PredicateTranslator.cs
- PredicateTranslatorTests.cs
However you could try just doing a text search with the Builder<>:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
var cursor = await aCollection.FindAsync(Builders<MyTypeSerializable>.Filter.Text("search"));
var results = await cursor.ToListAsync();
Details about the text filter is here https://docs.mongodb.com/manual/reference/operator/query/text/
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I docursor.ToEnumerable().Where(e => e.Field == 1)and it will actually get "materialized" after a.ToList()at the end?
– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );
– Kevin Smith
Dec 7 '16 at 14:15
add a comment |
Looking at the PredicateTranslator within the C# MongoDB driver there isn't any expression that gets converted in to a text query. So you won't be able to achive a text query using a linq query.
- PredicateTranslator.cs
- PredicateTranslatorTests.cs
However you could try just doing a text search with the Builder<>:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
var cursor = await aCollection.FindAsync(Builders<MyTypeSerializable>.Filter.Text("search"));
var results = await cursor.ToListAsync();
Details about the text filter is here https://docs.mongodb.com/manual/reference/operator/query/text/
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I docursor.ToEnumerable().Where(e => e.Field == 1)and it will actually get "materialized" after a.ToList()at the end?
– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );
– Kevin Smith
Dec 7 '16 at 14:15
add a comment |
Looking at the PredicateTranslator within the C# MongoDB driver there isn't any expression that gets converted in to a text query. So you won't be able to achive a text query using a linq query.
- PredicateTranslator.cs
- PredicateTranslatorTests.cs
However you could try just doing a text search with the Builder<>:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
var cursor = await aCollection.FindAsync(Builders<MyTypeSerializable>.Filter.Text("search"));
var results = await cursor.ToListAsync();
Details about the text filter is here https://docs.mongodb.com/manual/reference/operator/query/text/
Looking at the PredicateTranslator within the C# MongoDB driver there isn't any expression that gets converted in to a text query. So you won't be able to achive a text query using a linq query.
- PredicateTranslator.cs
- PredicateTranslatorTests.cs
However you could try just doing a text search with the Builder<>:
MongoClient client = new MongoClient(_mongoConnectionString);
IMongoDatabase mongoDatabase = client.GetDatabase(DatabaseName);
var aCollection = mongoDatabase.GetCollection<MyTypeSerializable>(CollectionName);
var cursor = await aCollection.FindAsync(Builders<MyTypeSerializable>.Filter.Text("search"));
var results = await cursor.ToListAsync();
Details about the text filter is here https://docs.mongodb.com/manual/reference/operator/query/text/
edited Dec 1 '16 at 17:20
answered Dec 1 '16 at 16:55
Kevin SmithKevin Smith
5,82711530
5,82711530
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I docursor.ToEnumerable().Where(e => e.Field == 1)and it will actually get "materialized" after a.ToList()at the end?
– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );
– Kevin Smith
Dec 7 '16 at 14:15
add a comment |
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I docursor.ToEnumerable().Where(e => e.Field == 1)and it will actually get "materialized" after a.ToList()at the end?
– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );
– Kevin Smith
Dec 7 '16 at 14:15
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
Looks legit, I'll check it out and come back, thank you.
– user1713059
Dec 1 '16 at 17:08
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
If it works can you accept the answer :-)
– Kevin Smith
Dec 2 '16 at 15:19
Could you also tell me how to combine this approach with linq-based filters? Can I do
cursor.ToEnumerable().Where(e => e.Field == 1) and it will actually get "materialized" after a .ToList() at the end?– user1713059
Dec 7 '16 at 11:34
Could you also tell me how to combine this approach with linq-based filters? Can I do
cursor.ToEnumerable().Where(e => e.Field == 1) and it will actually get "materialized" after a .ToList() at the end?– user1713059
Dec 7 '16 at 11:34
Not sure you can mix and match Linq with Mongo Filters, try:
var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );– Kevin Smith
Dec 7 '16 at 14:15
Not sure you can mix and match Linq with Mongo Filters, try:
var builder = Builders<MyModel>.Filter; var filter = builder.And( builder.Text("search"), builder.Eq(x => x.Field, 1) );– Kevin Smith
Dec 7 '16 at 14:15
add a comment |
It's possible to modify MongoDb driver source code. Let me explain it to you:
- Consider "PredicateTranslator" does not convert linq Expression into "$text" query. However there is a Text() method "FilterDefinitionBuilder" class, "PredicateTranslator" does not know the entity class property has a text search index.
- You have to mark entity class property (a condition in predicate statement) with an Attribute. The attribute works to remark property has a full text search index.
- From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"
Let me show you some code:
At MongoDB.Bson project create an Attribute as shown below:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonFullTextSearchAttribute : AttributeAt your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:
public class History
[MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
public string ObjectJSON get; set;At MongoDB.Driver.Linq.Translators.QueryableTranslator.cs
Add a field that keeps entity class type in Expression>, as shown below:
private Type _sourceObjectTypeInExpression;Add a method to get entity class type as shown below:
private void GetObjectType(Expression node)
if (node.Type != null && node.Type.GenericTypeArguments != null && node.Type.GenericTypeArguments.Length > 0)
this._sourceObjectTypeInExpression = node.Type.GenericTypeArguments[0];
Replace "public static QueryableTranslation Translate()" method as shown below:
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
BindingFlags.NonPublic)
.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorParameterTypes))
.Single();
var constructorParameters = new object[] translator._stages, translator._outputSerializer ;
var model = (QueryableExecutionModel)constructorInfo.Invoke(constructorParameters);
return new QueryableTranslation(model, translator._resultTransformer);In TranslateWhere() method pass "_sourceObjectTypeInExpression" field to PredicateTranslator.Translate() static method
var predicateValue = PredicateTranslator.Translate(node.Predicate, _serializerRegistry, this._sourceObjectTypeInExpression);B. MongoDB.Driver.Linq.Translators.PredicateTranslator.cs
- Add a field: "private Type sourceObjectTypeInExpression = null;"- Replace constructor as shown below (there has to be only one constructor);
private PredicateTranslator(Type _sourceObjectTypeInExpression)
this.sourceObjectTypeInExpression = _sourceObjectTypeInExpression;
- Replace function "public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry)" as shown below;
public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry, Type sourceObjectTypeInExpression)
var translator = new PredicateTranslator(sourceObjectTypeInExpression);
node = FieldExpressionFlattener.FlattenFields(node);
return translator.Translate(node)
.Render(serializerRegistry.GetSerializer<BsonDocument>(), serializerRegistry);
- Add these lines for reflection cache:
#region FullTextSearch
private static readonly object mSysncFullTextSearchObjectCache = new object();
private static ConcurrentDictionary<string, List<string>> _fullTextSearchObjectCache = null;
private static ConcurrentDictionary<string, List<string>> FullTextSearchObjectCache
get
if (_fullTextSearchObjectCache == null)
lock (mSysncFullTextSearchObjectCache)
try
if (_fullTextSearchObjectCache == null)
_fullTextSearchObjectCache = new ConcurrentDictionary<string, List<string>>();
finally
Monitor.PulseAll(mSysncFullTextSearchObjectCache);
return _fullTextSearchObjectCache;
private bool IsFullTextSearchProp(Type entityType, string propName)
bool retVal = false;
string entityName = entityType.Name;
this.SetObject2FullTextSearchObjectCache(entityType);
if (FullTextSearchObjectCache.ContainsKey(entityName))
List<string> x = FullTextSearchObjectCache[entityName];
retVal = x.Any(p => p == propName);
return retVal;
private void SetObject2FullTextSearchObjectCache(Type entityType)
string entityName = entityType.Name;
if (!FullTextSearchObjectCache.ContainsKey(entityName))
BindingFlags.Instance);
foreach (FieldInfo tmp in currentFields)
var attributes = tmp.GetCustomAttributes();
BsonFullTextSearchAttribute x = (BsonFullTextSearchAttribute)attributes.FirstOrDefault(a => typeof(BsonFullTextSearchAttribute) == a.GetType());
if (x != null)
retVal.Add(tmp.Name);
FullTextSearchObjectCache.AddOrUpdate(entityName, retVal, (k, v) => v);
#endregion
- Replace "switch (operatorType)" switch in "private FilterDefinition<BsonDocument> TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, fieldExpression.FieldName);
switch (operatorType)
case ExpressionType.Equal:
if (!isFullTextSearchProp)
return __builder.Eq(fieldExpression.FieldName, serializedValue);
else
return __builder.Text(serializedValue.ToString());
case ExpressionType.GreaterThan: return __builder.Gt(fieldExpression.FieldName, serializedValue);
case ExpressionType.GreaterThanOrEqual: return __builder.Gte(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThan: return __builder.Lt(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThanOrEqual: return __builder.Lte(fieldExpression.FieldName, serializedValue);
case ExpressionType.NotEqual:
if (!isFullTextSearchProp)
return __builder.Ne(fieldExpression.FieldName, serializedValue);
else
throw new ApplicationException(string.Format("Cannot use "NotEqual" on FullTextSearch property: "0"", fieldExpression.FieldName));
- Replace "switch (methodCallExpression.Method.Name)" switch in "private FilterDefinition<BsonDocument> TranslateStringQuery(MethodCallExpression methodCallExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, tmpFieldExpression.FieldName);
var pattern = Regex.Escape((string)constantExpression.Value);
if (!isFullTextSearchProp)
switch (methodCallExpression.Method.Name)
case "Contains": pattern = ".*" + pattern + ".*"; break;
case "EndsWith": pattern = ".*" + pattern; break;
case "StartsWith": pattern = pattern + ".*"; break; // query optimizer will use index for rooted regular expressions
default: return null;
var caseInsensitive = false;
MethodCallExpression stringMethodCallExpression;
while ((stringMethodCallExpression = stringExpression as MethodCallExpression) != null)
trimEnd)
var trimCharsPattern = GetTrimCharsPattern(trimCharsExpression);
if (trimCharsPattern == null)
return null;
if (trimStart)
pattern = trimCharsPattern + pattern;
if (trimEnd)
pattern = pattern + trimCharsPattern;
stringExpression = stringMethodCallExpression.Object;
pattern = "^" + pattern + "$";
if (pattern.StartsWith("^.*"))
pattern = pattern.Substring(3);
if (pattern.EndsWith(".*$"))
pattern = pattern.Substring(0, pattern.Length - 3);
var fieldExpression = GetFieldExpression(stringExpression);
var options = caseInsensitive ? "is" : "s";
return __builder.Regex(fieldExpression.FieldName, new BsonRegularExpression(pattern, options));
else
return __builder.Text(pattern);
add a comment |
It's possible to modify MongoDb driver source code. Let me explain it to you:
- Consider "PredicateTranslator" does not convert linq Expression into "$text" query. However there is a Text() method "FilterDefinitionBuilder" class, "PredicateTranslator" does not know the entity class property has a text search index.
- You have to mark entity class property (a condition in predicate statement) with an Attribute. The attribute works to remark property has a full text search index.
- From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"
Let me show you some code:
At MongoDB.Bson project create an Attribute as shown below:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonFullTextSearchAttribute : AttributeAt your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:
public class History
[MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
public string ObjectJSON get; set;At MongoDB.Driver.Linq.Translators.QueryableTranslator.cs
Add a field that keeps entity class type in Expression>, as shown below:
private Type _sourceObjectTypeInExpression;Add a method to get entity class type as shown below:
private void GetObjectType(Expression node)
if (node.Type != null && node.Type.GenericTypeArguments != null && node.Type.GenericTypeArguments.Length > 0)
this._sourceObjectTypeInExpression = node.Type.GenericTypeArguments[0];
Replace "public static QueryableTranslation Translate()" method as shown below:
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
BindingFlags.NonPublic)
.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorParameterTypes))
.Single();
var constructorParameters = new object[] translator._stages, translator._outputSerializer ;
var model = (QueryableExecutionModel)constructorInfo.Invoke(constructorParameters);
return new QueryableTranslation(model, translator._resultTransformer);In TranslateWhere() method pass "_sourceObjectTypeInExpression" field to PredicateTranslator.Translate() static method
var predicateValue = PredicateTranslator.Translate(node.Predicate, _serializerRegistry, this._sourceObjectTypeInExpression);B. MongoDB.Driver.Linq.Translators.PredicateTranslator.cs
- Add a field: "private Type sourceObjectTypeInExpression = null;"- Replace constructor as shown below (there has to be only one constructor);
private PredicateTranslator(Type _sourceObjectTypeInExpression)
this.sourceObjectTypeInExpression = _sourceObjectTypeInExpression;
- Replace function "public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry)" as shown below;
public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry, Type sourceObjectTypeInExpression)
var translator = new PredicateTranslator(sourceObjectTypeInExpression);
node = FieldExpressionFlattener.FlattenFields(node);
return translator.Translate(node)
.Render(serializerRegistry.GetSerializer<BsonDocument>(), serializerRegistry);
- Add these lines for reflection cache:
#region FullTextSearch
private static readonly object mSysncFullTextSearchObjectCache = new object();
private static ConcurrentDictionary<string, List<string>> _fullTextSearchObjectCache = null;
private static ConcurrentDictionary<string, List<string>> FullTextSearchObjectCache
get
if (_fullTextSearchObjectCache == null)
lock (mSysncFullTextSearchObjectCache)
try
if (_fullTextSearchObjectCache == null)
_fullTextSearchObjectCache = new ConcurrentDictionary<string, List<string>>();
finally
Monitor.PulseAll(mSysncFullTextSearchObjectCache);
return _fullTextSearchObjectCache;
private bool IsFullTextSearchProp(Type entityType, string propName)
bool retVal = false;
string entityName = entityType.Name;
this.SetObject2FullTextSearchObjectCache(entityType);
if (FullTextSearchObjectCache.ContainsKey(entityName))
List<string> x = FullTextSearchObjectCache[entityName];
retVal = x.Any(p => p == propName);
return retVal;
private void SetObject2FullTextSearchObjectCache(Type entityType)
string entityName = entityType.Name;
if (!FullTextSearchObjectCache.ContainsKey(entityName))
BindingFlags.Instance);
foreach (FieldInfo tmp in currentFields)
var attributes = tmp.GetCustomAttributes();
BsonFullTextSearchAttribute x = (BsonFullTextSearchAttribute)attributes.FirstOrDefault(a => typeof(BsonFullTextSearchAttribute) == a.GetType());
if (x != null)
retVal.Add(tmp.Name);
FullTextSearchObjectCache.AddOrUpdate(entityName, retVal, (k, v) => v);
#endregion
- Replace "switch (operatorType)" switch in "private FilterDefinition<BsonDocument> TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, fieldExpression.FieldName);
switch (operatorType)
case ExpressionType.Equal:
if (!isFullTextSearchProp)
return __builder.Eq(fieldExpression.FieldName, serializedValue);
else
return __builder.Text(serializedValue.ToString());
case ExpressionType.GreaterThan: return __builder.Gt(fieldExpression.FieldName, serializedValue);
case ExpressionType.GreaterThanOrEqual: return __builder.Gte(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThan: return __builder.Lt(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThanOrEqual: return __builder.Lte(fieldExpression.FieldName, serializedValue);
case ExpressionType.NotEqual:
if (!isFullTextSearchProp)
return __builder.Ne(fieldExpression.FieldName, serializedValue);
else
throw new ApplicationException(string.Format("Cannot use "NotEqual" on FullTextSearch property: "0"", fieldExpression.FieldName));
- Replace "switch (methodCallExpression.Method.Name)" switch in "private FilterDefinition<BsonDocument> TranslateStringQuery(MethodCallExpression methodCallExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, tmpFieldExpression.FieldName);
var pattern = Regex.Escape((string)constantExpression.Value);
if (!isFullTextSearchProp)
switch (methodCallExpression.Method.Name)
case "Contains": pattern = ".*" + pattern + ".*"; break;
case "EndsWith": pattern = ".*" + pattern; break;
case "StartsWith": pattern = pattern + ".*"; break; // query optimizer will use index for rooted regular expressions
default: return null;
var caseInsensitive = false;
MethodCallExpression stringMethodCallExpression;
while ((stringMethodCallExpression = stringExpression as MethodCallExpression) != null)
trimEnd)
var trimCharsPattern = GetTrimCharsPattern(trimCharsExpression);
if (trimCharsPattern == null)
return null;
if (trimStart)
pattern = trimCharsPattern + pattern;
if (trimEnd)
pattern = pattern + trimCharsPattern;
stringExpression = stringMethodCallExpression.Object;
pattern = "^" + pattern + "$";
if (pattern.StartsWith("^.*"))
pattern = pattern.Substring(3);
if (pattern.EndsWith(".*$"))
pattern = pattern.Substring(0, pattern.Length - 3);
var fieldExpression = GetFieldExpression(stringExpression);
var options = caseInsensitive ? "is" : "s";
return __builder.Regex(fieldExpression.FieldName, new BsonRegularExpression(pattern, options));
else
return __builder.Text(pattern);
add a comment |
It's possible to modify MongoDb driver source code. Let me explain it to you:
- Consider "PredicateTranslator" does not convert linq Expression into "$text" query. However there is a Text() method "FilterDefinitionBuilder" class, "PredicateTranslator" does not know the entity class property has a text search index.
- You have to mark entity class property (a condition in predicate statement) with an Attribute. The attribute works to remark property has a full text search index.
- From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"
Let me show you some code:
At MongoDB.Bson project create an Attribute as shown below:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonFullTextSearchAttribute : AttributeAt your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:
public class History
[MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
public string ObjectJSON get; set;At MongoDB.Driver.Linq.Translators.QueryableTranslator.cs
Add a field that keeps entity class type in Expression>, as shown below:
private Type _sourceObjectTypeInExpression;Add a method to get entity class type as shown below:
private void GetObjectType(Expression node)
if (node.Type != null && node.Type.GenericTypeArguments != null && node.Type.GenericTypeArguments.Length > 0)
this._sourceObjectTypeInExpression = node.Type.GenericTypeArguments[0];
Replace "public static QueryableTranslation Translate()" method as shown below:
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
BindingFlags.NonPublic)
.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorParameterTypes))
.Single();
var constructorParameters = new object[] translator._stages, translator._outputSerializer ;
var model = (QueryableExecutionModel)constructorInfo.Invoke(constructorParameters);
return new QueryableTranslation(model, translator._resultTransformer);In TranslateWhere() method pass "_sourceObjectTypeInExpression" field to PredicateTranslator.Translate() static method
var predicateValue = PredicateTranslator.Translate(node.Predicate, _serializerRegistry, this._sourceObjectTypeInExpression);B. MongoDB.Driver.Linq.Translators.PredicateTranslator.cs
- Add a field: "private Type sourceObjectTypeInExpression = null;"- Replace constructor as shown below (there has to be only one constructor);
private PredicateTranslator(Type _sourceObjectTypeInExpression)
this.sourceObjectTypeInExpression = _sourceObjectTypeInExpression;
- Replace function "public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry)" as shown below;
public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry, Type sourceObjectTypeInExpression)
var translator = new PredicateTranslator(sourceObjectTypeInExpression);
node = FieldExpressionFlattener.FlattenFields(node);
return translator.Translate(node)
.Render(serializerRegistry.GetSerializer<BsonDocument>(), serializerRegistry);
- Add these lines for reflection cache:
#region FullTextSearch
private static readonly object mSysncFullTextSearchObjectCache = new object();
private static ConcurrentDictionary<string, List<string>> _fullTextSearchObjectCache = null;
private static ConcurrentDictionary<string, List<string>> FullTextSearchObjectCache
get
if (_fullTextSearchObjectCache == null)
lock (mSysncFullTextSearchObjectCache)
try
if (_fullTextSearchObjectCache == null)
_fullTextSearchObjectCache = new ConcurrentDictionary<string, List<string>>();
finally
Monitor.PulseAll(mSysncFullTextSearchObjectCache);
return _fullTextSearchObjectCache;
private bool IsFullTextSearchProp(Type entityType, string propName)
bool retVal = false;
string entityName = entityType.Name;
this.SetObject2FullTextSearchObjectCache(entityType);
if (FullTextSearchObjectCache.ContainsKey(entityName))
List<string> x = FullTextSearchObjectCache[entityName];
retVal = x.Any(p => p == propName);
return retVal;
private void SetObject2FullTextSearchObjectCache(Type entityType)
string entityName = entityType.Name;
if (!FullTextSearchObjectCache.ContainsKey(entityName))
BindingFlags.Instance);
foreach (FieldInfo tmp in currentFields)
var attributes = tmp.GetCustomAttributes();
BsonFullTextSearchAttribute x = (BsonFullTextSearchAttribute)attributes.FirstOrDefault(a => typeof(BsonFullTextSearchAttribute) == a.GetType());
if (x != null)
retVal.Add(tmp.Name);
FullTextSearchObjectCache.AddOrUpdate(entityName, retVal, (k, v) => v);
#endregion
- Replace "switch (operatorType)" switch in "private FilterDefinition<BsonDocument> TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, fieldExpression.FieldName);
switch (operatorType)
case ExpressionType.Equal:
if (!isFullTextSearchProp)
return __builder.Eq(fieldExpression.FieldName, serializedValue);
else
return __builder.Text(serializedValue.ToString());
case ExpressionType.GreaterThan: return __builder.Gt(fieldExpression.FieldName, serializedValue);
case ExpressionType.GreaterThanOrEqual: return __builder.Gte(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThan: return __builder.Lt(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThanOrEqual: return __builder.Lte(fieldExpression.FieldName, serializedValue);
case ExpressionType.NotEqual:
if (!isFullTextSearchProp)
return __builder.Ne(fieldExpression.FieldName, serializedValue);
else
throw new ApplicationException(string.Format("Cannot use "NotEqual" on FullTextSearch property: "0"", fieldExpression.FieldName));
- Replace "switch (methodCallExpression.Method.Name)" switch in "private FilterDefinition<BsonDocument> TranslateStringQuery(MethodCallExpression methodCallExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, tmpFieldExpression.FieldName);
var pattern = Regex.Escape((string)constantExpression.Value);
if (!isFullTextSearchProp)
switch (methodCallExpression.Method.Name)
case "Contains": pattern = ".*" + pattern + ".*"; break;
case "EndsWith": pattern = ".*" + pattern; break;
case "StartsWith": pattern = pattern + ".*"; break; // query optimizer will use index for rooted regular expressions
default: return null;
var caseInsensitive = false;
MethodCallExpression stringMethodCallExpression;
while ((stringMethodCallExpression = stringExpression as MethodCallExpression) != null)
trimEnd)
var trimCharsPattern = GetTrimCharsPattern(trimCharsExpression);
if (trimCharsPattern == null)
return null;
if (trimStart)
pattern = trimCharsPattern + pattern;
if (trimEnd)
pattern = pattern + trimCharsPattern;
stringExpression = stringMethodCallExpression.Object;
pattern = "^" + pattern + "$";
if (pattern.StartsWith("^.*"))
pattern = pattern.Substring(3);
if (pattern.EndsWith(".*$"))
pattern = pattern.Substring(0, pattern.Length - 3);
var fieldExpression = GetFieldExpression(stringExpression);
var options = caseInsensitive ? "is" : "s";
return __builder.Regex(fieldExpression.FieldName, new BsonRegularExpression(pattern, options));
else
return __builder.Text(pattern);
It's possible to modify MongoDb driver source code. Let me explain it to you:
- Consider "PredicateTranslator" does not convert linq Expression into "$text" query. However there is a Text() method "FilterDefinitionBuilder" class, "PredicateTranslator" does not know the entity class property has a text search index.
- You have to mark entity class property (a condition in predicate statement) with an Attribute. The attribute works to remark property has a full text search index.
- From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"
Let me show you some code:
At MongoDB.Bson project create an Attribute as shown below:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonFullTextSearchAttribute : AttributeAt your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:
public class History
[MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
public string ObjectJSON get; set;At MongoDB.Driver.Linq.Translators.QueryableTranslator.cs
Add a field that keeps entity class type in Expression>, as shown below:
private Type _sourceObjectTypeInExpression;Add a method to get entity class type as shown below:
private void GetObjectType(Expression node)
if (node.Type != null && node.Type.GenericTypeArguments != null && node.Type.GenericTypeArguments.Length > 0)
this._sourceObjectTypeInExpression = node.Type.GenericTypeArguments[0];
Replace "public static QueryableTranslation Translate()" method as shown below:
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
BindingFlags.NonPublic)
.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorParameterTypes))
.Single();
var constructorParameters = new object[] translator._stages, translator._outputSerializer ;
var model = (QueryableExecutionModel)constructorInfo.Invoke(constructorParameters);
return new QueryableTranslation(model, translator._resultTransformer);In TranslateWhere() method pass "_sourceObjectTypeInExpression" field to PredicateTranslator.Translate() static method
var predicateValue = PredicateTranslator.Translate(node.Predicate, _serializerRegistry, this._sourceObjectTypeInExpression);B. MongoDB.Driver.Linq.Translators.PredicateTranslator.cs
- Add a field: "private Type sourceObjectTypeInExpression = null;"- Replace constructor as shown below (there has to be only one constructor);
private PredicateTranslator(Type _sourceObjectTypeInExpression)
this.sourceObjectTypeInExpression = _sourceObjectTypeInExpression;
- Replace function "public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry)" as shown below;
public static BsonDocument Translate(Expression node, IBsonSerializerRegistry serializerRegistry, Type sourceObjectTypeInExpression)
var translator = new PredicateTranslator(sourceObjectTypeInExpression);
node = FieldExpressionFlattener.FlattenFields(node);
return translator.Translate(node)
.Render(serializerRegistry.GetSerializer<BsonDocument>(), serializerRegistry);
- Add these lines for reflection cache:
#region FullTextSearch
private static readonly object mSysncFullTextSearchObjectCache = new object();
private static ConcurrentDictionary<string, List<string>> _fullTextSearchObjectCache = null;
private static ConcurrentDictionary<string, List<string>> FullTextSearchObjectCache
get
if (_fullTextSearchObjectCache == null)
lock (mSysncFullTextSearchObjectCache)
try
if (_fullTextSearchObjectCache == null)
_fullTextSearchObjectCache = new ConcurrentDictionary<string, List<string>>();
finally
Monitor.PulseAll(mSysncFullTextSearchObjectCache);
return _fullTextSearchObjectCache;
private bool IsFullTextSearchProp(Type entityType, string propName)
bool retVal = false;
string entityName = entityType.Name;
this.SetObject2FullTextSearchObjectCache(entityType);
if (FullTextSearchObjectCache.ContainsKey(entityName))
List<string> x = FullTextSearchObjectCache[entityName];
retVal = x.Any(p => p == propName);
return retVal;
private void SetObject2FullTextSearchObjectCache(Type entityType)
string entityName = entityType.Name;
if (!FullTextSearchObjectCache.ContainsKey(entityName))
BindingFlags.Instance);
foreach (FieldInfo tmp in currentFields)
var attributes = tmp.GetCustomAttributes();
BsonFullTextSearchAttribute x = (BsonFullTextSearchAttribute)attributes.FirstOrDefault(a => typeof(BsonFullTextSearchAttribute) == a.GetType());
if (x != null)
retVal.Add(tmp.Name);
FullTextSearchObjectCache.AddOrUpdate(entityName, retVal, (k, v) => v);
#endregion
- Replace "switch (operatorType)" switch in "private FilterDefinition<BsonDocument> TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, fieldExpression.FieldName);
switch (operatorType)
case ExpressionType.Equal:
if (!isFullTextSearchProp)
return __builder.Eq(fieldExpression.FieldName, serializedValue);
else
return __builder.Text(serializedValue.ToString());
case ExpressionType.GreaterThan: return __builder.Gt(fieldExpression.FieldName, serializedValue);
case ExpressionType.GreaterThanOrEqual: return __builder.Gte(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThan: return __builder.Lt(fieldExpression.FieldName, serializedValue);
case ExpressionType.LessThanOrEqual: return __builder.Lte(fieldExpression.FieldName, serializedValue);
case ExpressionType.NotEqual:
if (!isFullTextSearchProp)
return __builder.Ne(fieldExpression.FieldName, serializedValue);
else
throw new ApplicationException(string.Format("Cannot use "NotEqual" on FullTextSearch property: "0"", fieldExpression.FieldName));
- Replace "switch (methodCallExpression.Method.Name)" switch in "private FilterDefinition<BsonDocument> TranslateStringQuery(MethodCallExpression methodCallExpression)" function as shown below;
bool isFullTextSearchProp = this.IsFullTextSearchProp(this.sourceObjectTypeInExpression, tmpFieldExpression.FieldName);
var pattern = Regex.Escape((string)constantExpression.Value);
if (!isFullTextSearchProp)
switch (methodCallExpression.Method.Name)
case "Contains": pattern = ".*" + pattern + ".*"; break;
case "EndsWith": pattern = ".*" + pattern; break;
case "StartsWith": pattern = pattern + ".*"; break; // query optimizer will use index for rooted regular expressions
default: return null;
var caseInsensitive = false;
MethodCallExpression stringMethodCallExpression;
while ((stringMethodCallExpression = stringExpression as MethodCallExpression) != null)
trimEnd)
var trimCharsPattern = GetTrimCharsPattern(trimCharsExpression);
if (trimCharsPattern == null)
return null;
if (trimStart)
pattern = trimCharsPattern + pattern;
if (trimEnd)
pattern = pattern + trimCharsPattern;
stringExpression = stringMethodCallExpression.Object;
pattern = "^" + pattern + "$";
if (pattern.StartsWith("^.*"))
pattern = pattern.Substring(3);
if (pattern.EndsWith(".*$"))
pattern = pattern.Substring(0, pattern.Length - 3);
var fieldExpression = GetFieldExpression(stringExpression);
var options = caseInsensitive ? "is" : "s";
return __builder.Regex(fieldExpression.FieldName, new BsonRegularExpression(pattern, options));
else
return __builder.Text(pattern);
edited Feb 8 '18 at 5:36
Jonny
9281720
9281720
answered Feb 8 '18 at 4:01
Tolga KabadurmusTolga Kabadurmus
1
1
add a comment |
add a comment |
Searching for solution I found FilterDefinition<T>.Inject() extension method.
So we can go deeper and create one more extension on IMongoQueryable<T>:
public static class MongoQueryableFullTextExtensions
public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search)
var filter = Builders<T>.Filter.Text(search);
return query.Where(_ => filter.Inject());
And use it like this:
IMongoDatabase database = GetMyDatabase();
var results = database
.GetCollection<Blog>("Blogs")
.AsQueryable()
.WhereText("stackoverflow")
.Take(10)
.ToArray();
Hope this helps somebody :)
add a comment |
Searching for solution I found FilterDefinition<T>.Inject() extension method.
So we can go deeper and create one more extension on IMongoQueryable<T>:
public static class MongoQueryableFullTextExtensions
public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search)
var filter = Builders<T>.Filter.Text(search);
return query.Where(_ => filter.Inject());
And use it like this:
IMongoDatabase database = GetMyDatabase();
var results = database
.GetCollection<Blog>("Blogs")
.AsQueryable()
.WhereText("stackoverflow")
.Take(10)
.ToArray();
Hope this helps somebody :)
add a comment |
Searching for solution I found FilterDefinition<T>.Inject() extension method.
So we can go deeper and create one more extension on IMongoQueryable<T>:
public static class MongoQueryableFullTextExtensions
public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search)
var filter = Builders<T>.Filter.Text(search);
return query.Where(_ => filter.Inject());
And use it like this:
IMongoDatabase database = GetMyDatabase();
var results = database
.GetCollection<Blog>("Blogs")
.AsQueryable()
.WhereText("stackoverflow")
.Take(10)
.ToArray();
Hope this helps somebody :)
Searching for solution I found FilterDefinition<T>.Inject() extension method.
So we can go deeper and create one more extension on IMongoQueryable<T>:
public static class MongoQueryableFullTextExtensions
public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search)
var filter = Builders<T>.Filter.Text(search);
return query.Where(_ => filter.Inject());
And use it like this:
IMongoDatabase database = GetMyDatabase();
var results = database
.GetCollection<Blog>("Blogs")
.AsQueryable()
.WhereText("stackoverflow")
.Take(10)
.ToArray();
Hope this helps somebody :)
answered Mar 21 at 23:23
The SmallestThe Smallest
4,8892033
4,8892033
add a comment |
add a comment |
How about:
IMongoQueryable<MyTypeSerializable> queryable = aCollection
.AsQueryable()
.Where(e=> e.Field.Contains("term"));
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
add a comment |
How about:
IMongoQueryable<MyTypeSerializable> queryable = aCollection
.AsQueryable()
.Where(e=> e.Field.Contains("term"));
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
add a comment |
How about:
IMongoQueryable<MyTypeSerializable> queryable = aCollection
.AsQueryable()
.Where(e=> e.Field.Contains("term"));
How about:
IMongoQueryable<MyTypeSerializable> queryable = aCollection
.AsQueryable()
.Where(e=> e.Field.Contains("term"));
answered Dec 1 '16 at 16:32
Vitaliy KalininVitaliy Kalinin
1,116617
1,116617
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
add a comment |
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
Are you sure this utilizes the text index?
– user1713059
Dec 1 '16 at 17:06
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
It depends on 2 factors: implementation of Linq provider and query processor on mongodb side. In order to be sure you have to trace this chain.
– Vitaliy Kalinin
Dec 1 '16 at 17:24
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
Looking though the source code the above doesn't utilise the text query within mongodb.
– Kevin Smith
Dec 2 '16 at 9:30
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40915209%2fmongodb-net-driver-and-text-search%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown