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;








1















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?










share|improve this question




























    1















    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?










    share|improve this question
























      1












      1








      1








      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?










      share|improve this question














      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Dec 1 '16 at 16:03









      user1713059user1713059

      3721618




      3721618






















          4 Answers
          4






          active

          oldest

          votes


















          1














          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/






          share|improve this answer

























          • 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 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



















          0














          It's possible to modify MongoDb driver source code. Let me explain it to you:



          1. 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.

          2. 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.

          3. From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"

          Let me show you some code:




          1. At MongoDB.Bson project create an Attribute as shown below:



            [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
            public class BsonFullTextSearchAttribute : Attribute




          2. At your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:



            public class History 

            [MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
            public string ObjectJSON get; set;




          3. 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);








          share|improve this answer
































            0














            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 :)






            share|improve this answer






























              -1














              How about:



              IMongoQueryable<MyTypeSerializable> queryable = aCollection
              .AsQueryable()
              .Where(e=> e.Field.Contains("term"));





              share|improve this answer























              • 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











              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
              );



              );













              draft saved

              draft discarded


















              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









              1














              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/






              share|improve this answer

























              • 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 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
















              1














              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/






              share|improve this answer

























              • 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 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














              1












              1








              1







              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/






              share|improve this answer















              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/







              share|improve this answer














              share|improve this answer



              share|improve this answer








              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 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


















              • 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 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

















              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














              0














              It's possible to modify MongoDb driver source code. Let me explain it to you:



              1. 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.

              2. 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.

              3. From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"

              Let me show you some code:




              1. At MongoDB.Bson project create an Attribute as shown below:



                [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
                public class BsonFullTextSearchAttribute : Attribute




              2. At your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:



                public class History 

                [MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
                public string ObjectJSON get; set;




              3. 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);








              share|improve this answer





























                0














                It's possible to modify MongoDb driver source code. Let me explain it to you:



                1. 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.

                2. 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.

                3. From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"

                Let me show you some code:




                1. At MongoDB.Bson project create an Attribute as shown below:



                  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
                  public class BsonFullTextSearchAttribute : Attribute




                2. At your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:



                  public class History 

                  [MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
                  public string ObjectJSON get; set;




                3. 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);








                share|improve this answer



























                  0












                  0








                  0







                  It's possible to modify MongoDb driver source code. Let me explain it to you:



                  1. 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.

                  2. 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.

                  3. From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"

                  Let me show you some code:




                  1. At MongoDB.Bson project create an Attribute as shown below:



                    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
                    public class BsonFullTextSearchAttribute : Attribute




                  2. At your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:



                    public class History 

                    [MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
                    public string ObjectJSON get; set;




                  3. 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);








                  share|improve this answer















                  It's possible to modify MongoDb driver source code. Let me explain it to you:



                  1. 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.

                  2. 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.

                  3. From now on "PredicateTranslator" class knows the property has a full text search index with this Attribute "PredicateTranslator"

                  Let me show you some code:




                  1. At MongoDB.Bson project create an Attribute as shown below:



                    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
                    public class BsonFullTextSearchAttribute : Attribute




                  2. At your entity class property place the "BsonFullTextSearchAttribute" Attribute as shown below:



                    public class History 

                    [MongoDB.Bson.Serialization.Attributes.BsonFullTextSearch]
                    public string ObjectJSON get; set;




                  3. 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);









                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Feb 8 '18 at 5:36









                  Jonny

                  9281720




                  9281720










                  answered Feb 8 '18 at 4:01









                  Tolga KabadurmusTolga Kabadurmus

                  1




                  1





















                      0














                      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 :)






                      share|improve this answer



























                        0














                        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 :)






                        share|improve this answer

























                          0












                          0








                          0







                          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 :)






                          share|improve this answer













                          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 :)







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Mar 21 at 23:23









                          The SmallestThe Smallest

                          4,8892033




                          4,8892033





















                              -1














                              How about:



                              IMongoQueryable<MyTypeSerializable> queryable = aCollection
                              .AsQueryable()
                              .Where(e=> e.Field.Contains("term"));





                              share|improve this answer























                              • 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















                              -1














                              How about:



                              IMongoQueryable<MyTypeSerializable> queryable = aCollection
                              .AsQueryable()
                              .Where(e=> e.Field.Contains("term"));





                              share|improve this answer























                              • 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













                              -1












                              -1








                              -1







                              How about:



                              IMongoQueryable<MyTypeSerializable> queryable = aCollection
                              .AsQueryable()
                              .Where(e=> e.Field.Contains("term"));





                              share|improve this answer













                              How about:



                              IMongoQueryable<MyTypeSerializable> queryable = aCollection
                              .AsQueryable()
                              .Where(e=> e.Field.Contains("term"));






                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              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

















                              • 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

















                              draft saved

                              draft discarded
















































                              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.




                              draft saved


                              draft discarded














                              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





















































                              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







                              Popular posts from this blog

                              SQL error code 1064 with creating Laravel foreign keysForeign key constraints: When to use ON UPDATE and ON DELETEDropping column with foreign key Laravel error: General error: 1025 Error on renameLaravel SQL Can't create tableLaravel Migration foreign key errorLaravel php artisan migrate:refresh giving a syntax errorSQLSTATE[42S01]: Base table or view already exists or Base table or view already exists: 1050 Tableerror in migrating laravel file to xampp serverSyntax error or access violation: 1064:syntax to use near 'unsigned not null, modelName varchar(191) not null, title varchar(191) not nLaravel cannot create new table field in mysqlLaravel 5.7:Last migration creates table but is not registered in the migration table

                              용인 삼성생명 블루밍스 목차 통계 역대 감독 선수단 응원단 경기장 같이 보기 외부 링크 둘러보기 메뉴samsungblueminx.comeh선수 명단용인 삼성생명 블루밍스용인 삼성생명 블루밍스ehsamsungblueminx.comeheheheh

                              155 수학 과학 기타 둘러보기 메뉴eh추가해eh문서를 완성해