Index hint with mongodb csharp - mongodb

I am migrating from the mongodb csharp driver 1.10.0 to 2.0.0.
One of the collection I am using is very big and has to fulfill many queries with different filter attributes. That is why I was relying on some index hint statements. With the v1.10 driver it looks like
myCollection.Find(query).SetHint("myIndexName");
I searched the v2 driver api but this hint method seems to be completly removed in the v2 driver. Is there an alternative? How should I do index hints with the v2 driver?
Note: The Solutions provided works for latest mongodb csharp drivers as well

You can use the FindOptions.Modifiers property.
var modifiers = new BsonDocument("$hint", "myIndexName");
await myCollection.Find(query, new FindOptions { Modifiers = modifiers }).ToListAsync();
May I ask why you are using the hint? Was the server consistently choosing the wrong index? You shouldn't need to do this except in exceptional cases.
Craig

Ideally, try to make the query in a way that mongodb optimizer can use the index automatically.
If you are using FindAsync then you will have a property named Hint. Use it like this:
If you have index named "myIndexName" which you want your query should use forcefully, then use like this:.
BsonString bsonString = new BsonString("myIndexName");
cursor = await collection.FindAsync(y => y.Population > 400000000,
new FindOptions<Person, Person>()
{
BatchSize = 200,
NoCursorTimeout = true,
AllowPartialResults = true,
Projection = "{'_id':1,'Name':1,'Population':1}"
Hint = bsonString.AsBsonValue,
}).ConfigureAwait(false);
You can fine BsonString class in MongoDB.Bson

With agregate you can force indice like this:
BsonString bsonString = new BsonString("ix_indice");
var query = this.collection.Aggregate(new AggregateOptions() { Hint = bsonString }).Match(new BsonDocument {..});

If you are using the Linq IQueryable, you can specify the hint (and other options) like this:
BsonDocument hint = new BsonDocument("myFieldName", 1);
// or
BsonDocument hint = new BsonString("myIndexName");
await collection.AsQueryable(new AggregateOptions { Hint = hint })
myFieldName can also reference a complex field, e.g. Metadata.FileName
myIndexName is the name of an index. I prefer to reference the field (first option) directly, instead of an index, for simple cases.

Related

Checking if an Index exists in mongodb

Is there a command that i can use via javascript in mongo shell that can be used to check if the particular index exists in my mongodb. I am building a script file that would create indexes. I would like that if I run this file multiple number of times then the indexes that already exists are not recreated.
I can use db.collection.getIndexes() to get the collection of all the indexes in my db and then build a logic to ignore the ones that already exists but i was wondering if there is command to get an index and then ignore a script that creates the index. Something like:
If !exists(db.collection.exists("indexname"))
{
create db.collectionName.CreateIndex("IndexName")
}
Creating indexes in MongoDB is an idempotent operation. So running db.names.createIndex({name:1}) would create the index only if it didn't already exist.
The deprecated (as of MongoDB 3.0) alias for createIndex() is ensureIndex() which is a bit clearer on what createIndex() actually does.
Edit:
Thanks to ZitRo for clarifying in comments that calling createIndex() with the same name but different options than an existing index will throw an error MongoError: Index with name: **indexName** already exists with different options as explained in this question.
If you have other reasons for checking, then you can access current index data one of two ways:
As of v3.0, we can use db.names.getIndexes() where names is the name of the collection. Docs here.
Before v3.0, you can access the system.indexes collection and do a find as bri describes below.
Use db.system.indexes and search on it.
If, for example, you have an index called 'indexname', you can search for it like this:
db.system.indexes.find({'name':'indexname'});
If you need to search for that index on a specific collection,then you need to use the ns property (and, it would be helpful to have the db name).
db.system.indexes.find({'name':'indexname', 'ns':'dbname.collection'});
Or, if you absolutely hate including the db name...
db.system.indexes.find({'name':'indexname', 'ns': {$regex:'.collection$'}});
Pulling that together...
So, you're finished check would be:
if(db.system.indexes.find({name:'indexname',ns:{$regex:'.collection$'}}).count()==0) {
db.collection.createIndex({blah:1},{name:'indexname'})
}
Using nodeJS MongoDB driver version 2.2:
const MongoClient = require('mongodb').MongoClient;
exports.dropOldIndexIfExist = dropOldIndexIfExist;
async function dropOldIndexIfExist() {
try {
const mongoConnection = MongoClient.connect('mongodb://localhost:27017/test');
const indexName = 'name_1';
const isIndexExist = await mongoConnection.indexExists(indexName);
if (isIndexExist === true) {
await mongoConnection.dropIndex(indexName);
}
} catch (err) {
console.error('dropOldIndexIfExist', err.message);
throw err;
}
}
I've created a custom method in c# to check if the index exists, using mongo driver:
public bool IndexExists<TDocument>(
IMongoCollection<TDocument> collection, string name)
{
var indexes = collection.Indexes.List().ToList();
var indexNames = indexes
.SelectMany(index => index.Elements)
.Where(element => element.Name == "name")
.Select(name => name.Value.ToString());
return indexNames.Contains(name);
}
maybe we can use something like https://docs.mongodb.com/v3.2/reference/method/db.collection.getIndexes/#db.collection.getIndexes to check if the collection have an index equal to something ?
if yes then drop and add the new one or add the new one directly
In my case i did as follows.
DBCollection yourcollectionName = mt.getCollection("your_collection");
if (yourcollectionName.getIndexInfo() == null || yourcollectionName.getIndexInfo().isEmpty()) {
DBObject indexOptions = new BasicDBObject();
indexOptions.put("pro1", 1);
indexOptions.put("pro2", 1);
yourcollectionName.createIndex(indexOptions, "name_of_your_index", true);
}
Here is a Python 3.5+ and pymongo >= 4.1 (type hints) function that I wrote which checks to see if the index name exists (other details about the index are omitted).
from pymongo import MongoClient
from pymongo.collection import Collection
def check_collection_indexes(db: MongoClient, collection_name: str, index_name: str) -> bool:
coll: Collection = db[collection_name]
indexes: dict = coll.index_information()
# assume false
found = False
# get length of the index name for substring
l = len(index_name)
for k in indexes.keys():
# Substring the keys and check for match
if k[:l] == index_name:
found = True
break
else:
found = False
return found
If the index exists it will return True, otherwise you can use the False output to call another function that creates/recreates the indexes.

In mongodb version 3 using java api how do we give a hint while querying

In mongodb version 3 using java api how do we give a hint while querying
The query result is FindIterable which has MongoCursor.
How should I give a hint to use a particular index.
With older versions there is DBCursor which have API's for hint.
modifiers should be used:
BasicDBObject index = new BasicDBObject("num", 1);
BasicDBObject hint = new BasicDBObject("$hint", index);
FindIterable<Document> iterable = collection.find().modifiers(hint);
You need to use FindIterable.modifiers method to specify $hint:
private static void testHint(MongoDatabase db) {
MongoCollection<Document> col = db.getCollection("Stores");
FindIterable<Document> iterable = col.find(new Document("store","storenumbervalue"));
iterable.modifiers(new Document("$hint","index_name"));
MongoCursor curs = iterable.iterator();
while(curs.hasNext()){
System.out.println(curs.next());
}
}
index_name - actual index name in mongo.

MongoDB C# Query for 'NotLike' on string

I'm using official Mongo C# driver. As suggested in answer to one question I'm using the following for the 'like' operator -
Query.Matches("name", "Joe");
My question is how can I achieve the 'NotLike' functionality ?
Assuming you are using the new Query builder in version 1.5, you would do it this way:
var query = Query.Not(Query.Matches("name", "Joe"));
In version 1.5 we also introduced a new typed Query builder, which you could use this way:
var query = Query.Not(Query<C>.Matches(x => x.Name, "Joe"));
Finally, you could also write a LINQ query:
var query = collection.AsQueryable<C>().Where(x => !Regex.IsMatch(x.Name, "Joe"));

How to use mongo database queries?

I am using MongoDb for my c sharp project instead of mysql, now i want to use query like select * from student Where (name is null or name='XXX') and (sno is null or sno=10), how can i build this query in mongodb.
thanks,
#dinnu.
This should get you started:
var mongoServer = MongoDB.Driver.MongoServer.Create("mongodb://localhost?safe=true");
var mongoDatabase = mongoServer.GetDatabase("test");
var mongoCollection = mongoDatabase.GetCollection<TModel>("Test");
var cursor = mongoCollection.Find(Query.And(
Query.Or(
Query.EQ("Name", "xxx"),
Query.EQ("Name", null)),
Query.Or(
Query.EQ("sno", 10)),
Query.EQ("sno", null)));
Where TModel is the type of the class you want to deserialize from the db. Now you can use cursor to iterate the results of that query, for example:
var someModel = cursor.FirstOrDefault();
Take a look at Fluent Mongo (https://github.com/craiggwilson/fluent-mongo). It adds Linq on top of the official 10gen driver. Thus far I have found using it to be a good experience. It is available via the Nuget or the GitHub.

How do I get the date a MongoDB collection was created using MongoDB C# driver?

I need to iterate through all of the collections in my MongoDB database and get the time when each of the collections was created (I understand that I could get the timestamp of each object in the collection, but I would rather not go that route if a simpler/faster method exists).
This should give you an idea of what I'm trying to do:
MongoDatabase _database;
// code elided
var result = _database.GetAllCollectionNames().Select(collectionName =>
{
_database.GetCollection( collectionName ) //.{GetCreatedDate())
});
As far as I know, MongoDB doesn't keep track of collection creation dates. However, it's really easy to do this yourself. Add a simple method, something like this, and use it whenever you create a new collection:
public static void CreateCollectionWithMetadata(string collectionName)
{
var result = _db.CreateCollection(collectionName);
if (result.Ok)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
collectionMetadata.Insert(new { Id = collectionName, Created = DateTime.Now });
}
}
Then whenever you need the information just query the collectionMetadata collection. Or, if you want to use an extension method like in your example, do something like this:
public static DateTime GetCreatedDate(this MongoCollection collection)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
var metadata = collectionMetadata.FindOneById(collection.Name);
var created = metadata["Created"].AsDateTime;
return created;
}
The "creation date" is not part of the collection's metadata. A collection does not "know" when it was created. Some indexes have an ObjectId() which implies a timestamp, but this is not consistent and not reliable.
Therefore, I don't believe this can be done.
Like Mr. Gates VP say, there is no way using the metadata... but you can get the oldest document in the collection and get it from the _id.
Moreover, you can insert an "empty" document in the collection for that purpose without recurring to maintain another collection.
And it's very easy get the oldest document:
old = db.collection.find({}, {_id}).sort({_id: 1}).limit(1)
dat = old._id.getTimestamp()
By default, all collection has an index over _id field, making the find efficient.
(I using MongoDb 3.6)
Seems like it's some necroposting but anyway: I tried to find an answer and got it:
Checked it in Mongo shell, don't know how to use in C#:
// db.payload_metadata.find().limit(1)
ObjectId("60379be2bec7a3c17e6b662b").getTimestamp()
ISODate("2021-02-25T12:45:22Z")