Checking if an Index exists in mongodb - 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.

Related

How can I upsert a record and array element at the same time?

That is meant to be read as a dual upsert operation, upsert the document then the array element.
So MongoDB is a denormalized store for me (we're event sourced) and one of the things I'm trying to deal with is the concurrent nature of that. The problem is this:
Events can come in out of order, so each update to the database need to be an upsert.
I need to be able to not only upsert the parent document but an element in an array property of that document.
For example:
If the document doesn't exist, create it. All events in this stream have the document's ID but only part of the information depending on the event.
If the document does exist, then update it. This is the easy part. The update command is just written as UpdateOneAsync and as an upsert.
If the event is actually to update a list, then that list element needs to be upserted. So if the document doesn't exist, it needs to be created and the list item will be upserted (resulting in an insert); if the document does exist, then we need to find the element and update it as an upsert, so if the element exists then it is updated otherwise it is inserted.
If at all possible, having it execute as a single atomic operation would be ideal, but if it can only be done in multiple steps, then so be it. I'm getting a number of mixed examples on the net due to the large change in the 2.x driver. Not sure what I'm looking for beyond the UpdateOneAsync. Currently using 2.4.x. Explained examples would be appreciated. TIA
Note:
Reiterating that this is a question regarding the MongoDB C# driver 2.4.x
Took some tinkering, but I got it.
var notificationData = new NotificationData
{
ReferenceId = e.ReferenceId,
NotificationId = e.NotificationId,
DeliveredDateUtc = e.SentDate.DateTime
};
var matchDocument = Builders<SurveyData>.Filter.Eq(s => s.SurveyId, e.EntityId);
// first upsert the document to make sure that you have a collection to write to
var surveyUpsert = new UpdateOneModel<SurveyData>(
matchDocument,
Builders<SurveyData>.Update
.SetOnInsert(f => f.SurveyId, e.EntityId)
.SetOnInsert(f => f.Notifications, new List<NotificationData>())){ IsUpsert = true};
// then push a new element if none of the existing elements match
var noMatchReferenceId = Builders<SurveyData>.Filter
.Not(Builders<SurveyData>.Filter.ElemMatch(s => s.Notifications, n => n.ReferenceId.Equals(e.ReferenceId)));
var insertNewNotification = new UpdateOneModel<SurveyData>(
matchDocument & noMatchReferenceId,
Builders<SurveyData>.Update
.Push(s => s.Notifications, notificationData));
// then update the element that does match the reference ID (if any)
var matchReferenceId = Builders<SurveyData>.Filter
.ElemMatch(s => s.Notifications, Builders<NotificationData>.Filter.Eq(n => n.ReferenceId, notificationData.ReferenceId));
var updateExistingNotification = new UpdateOneModel<SurveyData>(
matchDocument & matchReferenceId,
Builders<SurveyData>.Update
// apparently the mongo C# driver will convert any negative index into an index symbol ('$')
.Set(s => s.Notifications[-1].NotificationId, e.NotificationId)
.Set(s => s.Notifications[-1].DeliveredDateUtc, notificationData.DeliveredDateUtc));
// execute these as a batch and in order
var result = await _surveyRepository.DatabaseCollection
.BulkWriteAsync(
new []{ surveyUpsert, insertNewNotification, updateExistingNotification },
new BulkWriteOptions { IsOrdered = true })
.ConfigureAwait(false);
The post linked as being a dupe was absolutely helpful, but it was not the answer. There were a few things that needed to be discovered.
The 'second statement' in the linked example didn't work
correctly, at least when translated literally. To get it to work, I had to match on the
element and then invert the logic by wrapping it in the Not() filter.
In order to use 'this index' on the match, you have to use a
negative index on the array. As it turns out, the C# driver will
convert any negative index to the '$' character when the query is
rendered.
In order to ensure they are run in order, you must include bulk write
options with IsOrdered set to true.

ReferenceError: db is not defined while trying to find distinct entries in database

I am getting db is not defined when trying to use mongodb's distinct in meteor.
Template.displayinbox.helpers({
inboxlistings: function() {
itemscount = db.Messages.distinct( "fromUsername" ).count;
return db.Messages.distinct( "fromUsername" );
}
});
I want to be able to return only distinct documents in my collection from the username field and count all those documents that is posted by the fromUsername. How would I go about doing this in Meteor?
When you're querying anything in the Meteor code itself, you don't need to write db first. You have to use the variable name that used to instantiate the Mongo Object. Let's say you defined your Mongo db like this.
example = new Mongo.Collection('Messages');
then within your helper you just use the typical query using this object.
Template.displayinbox.helpers({
inboxlistings: function() {
var items = example.find();
return _uniq(items,function(i){return i.fromUserName;});
}
});

Index hint with mongodb csharp

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.

DB2 Nosql / MongoDB 'An operator that starts with $ is expected for projectid' error?

I have a list of documents with the structure:
{
from:"string"
to:"string"
payload:{
projectid:10000
}
}
I want to delete all documents with payload.projectid set to 10000 using the java API.
From the command-line I write:
db.notifications.find({"payload":{"projectid":10000}})
Error:
java.lang.RuntimeException: An operator that starts with $ is expected for projectid.
A similar error arises if I delete, whether through cli or programmatically.
This works in normal mongoDB.
How do I delete in db2 nosql?
Java code (for your reference!):
BasicDBObject query = new BasicDBObject();
query.append("to", "username");
query.append("from", "username2");
query.append("payload", new BasicDBObject().append("projectid", 10000));
System.out.println(query); // prints out okay
col.remove(query); //error
You should be using dot notation to reference the sub-document field:
Link
In your case it would be:
db.notifications.find({"payload.projectid":10000})
In Java the find should look something like this:
DBCollection notifications = db.getCollection("notifications")
DBObject subdocumentQuery = new BasicDBObject("payload.projectid", 10000);
DBCursor subdocumentCursor = notifications.find(subdocumentQuery);

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