/// <summary> /// Add a configuration key-value pair. /// </summary> /// <param name="key">The key.</param> /// <param name="val">The value.</param> public override void AddConfigValue(string key, string val) { if (String.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } if (String.IsNullOrEmpty(val)) { throw new ArgumentNullException(nameof(val)); } key = DedupeCommon.SanitizeString(key); val = DedupeCommon.SanitizeString(val); DbExpression e = new DbExpression( _ORM.GetColumnName <DedupeConfig>(nameof(DedupeConfig.Key)), DbOperators.Equals, key); DedupeConfig config = _ORM.SelectFirst <DedupeConfig>(e); if (config != null) { _ORM.Delete <DedupeConfig>(config); } config = new DedupeConfig(key, val); config = _ORM.Insert <DedupeConfig>(config); }
/* * Read operations cannot continue if the URL is being written, but can continue if being read elsewhere. * */ internal string AddReadLock(RequestMetadata md) { if (md == null) { throw new ArgumentNullException(nameof(md)); } if (WriteLockExists(md.Http.Request.Url.RawWithoutQuery)) { return(null); } UrlLock urlLock = null; DateTime expirationUtc = DateTime.Now.ToUniversalTime().AddSeconds(_Settings.Storage.LockExpirationSeconds); if (md.Params.ExpirationUtc != null) { expirationUtc = md.Params.ExpirationUtc.Value.ToUniversalTime(); } if (md.User != null && !String.IsNullOrEmpty(md.User.GUID)) { urlLock = new UrlLock(LockType.Read, md.Http.Request.Url.RawWithoutQuery, md.User.GUID, expirationUtc); } else { urlLock = new UrlLock(LockType.Read, md.Http.Request.Url.RawWithoutQuery, null, expirationUtc); } urlLock = _ORM.Insert <UrlLock>(urlLock); return(urlLock.GUID); }
internal bool AddUser(User user) { if (user == null) { throw new ArgumentNullException(nameof(user)); } User userByGuid = GetUserByGuid(user.GUID); if (userByGuid != null) { _Logging.Warn("ConfigManager AddUser user GUID " + user.GUID + " already exists"); return(false); } User userByEmail = GetUserByEmail(user.Email); if (userByEmail != null) { _Logging.Warn("ConfigManager AddUser user email " + user.Email + " already exists"); return(false); } _ORM.Insert <User>(user); return(true); }
internal void AddApiKey(ApiKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (ApiKeyExists(key.GUID)) { return; } _ORM.Insert <ApiKey>(key); }
internal bool AddReadLock(UrlLock urlLock) { if (urlLock == null) { throw new ArgumentNullException(nameof(urlLock)); } if (WriteLockExists(urlLock.Url)) { return(false); } _ORM.Insert <UrlLock>(urlLock); return(true); }
/// <summary> /// Add a user to a specified role. /// </summary> /// <param name="username">The name of the user.</param> /// <param name="rolename">The name of the role.</param> public void AddUserToRole(string username, string rolename) { if (String.IsNullOrEmpty(username)) { throw new ArgumentNullException(nameof(username)); } if (String.IsNullOrEmpty(rolename)) { throw new ArgumentNullException(nameof(rolename)); } UserRole u = new UserRole(username, rolename); _ORM.Insert <UserRole>(u); }
internal bool Add(Container container) { if (container == null) { throw new ArgumentNullException(nameof(container)); } if (String.IsNullOrEmpty(container.Name)) { return(false); } if (String.IsNullOrEmpty(container.UserGUID)) { return(false); } if (String.IsNullOrEmpty(container.GUID)) { return(false); } if (String.IsNullOrEmpty(container.ObjectsDirectory)) { container.ObjectsDirectory = _Settings.Storage.Directory + container.UserGUID + "/" + container.GUID + "/"; } container = _ORM.Insert <Container>(container); if (container == null) { return(false); } InitializeContainerClient(container); return(true); }
/// <summary> /// Add. /// </summary> /// <param name="user">User.</param> /// <param name="role">Role.</param> /// <returns>Object.</returns> public UserRole Add(User user, Role role) { if (user == null) { throw new ArgumentNullException(nameof(user)); } if (role == null) { throw new ArgumentNullException(nameof(role)); } if (!_Users.ExistsByName(user.Name)) { throw new KeyNotFoundException("The specified user was not found."); } if (!_Roles.ExistsByName(role.Name)) { throw new KeyNotFoundException("The specified role was not found."); } if (Exists(user, role)) { throw new ArgumentException("An item with the same keys has already been added."); } return(_ORM.Insert <UserRole>(new UserRole(user, role))); }
/// <summary> /// Creates an account with the specified name. /// </summary> /// <param name="name">Name of the account.</param> /// <param name="initialBalance">Initial balance of the account.</param> /// <returns>String containing the GUID of the newly-created account.</returns> public string CreateAccount(string name, decimal?initialBalance = null) { if (String.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } Account a = new Account(name); a = _ORM.Insert <Account>(a); try { LockAccount(a.GUID); Entry balance = new Entry(); balance.GUID = Guid.NewGuid().ToString(); balance.AccountGUID = a.GUID; balance.Type = EntryType.Balance; balance.Amount = 0m; if (initialBalance != null) { balance.Amount = initialBalance.Value; } balance.Description = "Initial balance"; balance.CommittedByGUID = null; balance.IsCommitted = true; DateTime ts = DateTime.Now.ToUniversalTime(); balance.CreatedUtc = ts; balance.CommittedUtc = ts; balance = _ORM.Insert <Entry>(balance); } finally { UnlockAccount(a.GUID); Task.Run(() => AccountCreated?.Invoke(this, new AccountEventArgs(a))); } return(a.GUID); }
/// <summary> /// Add. /// </summary> /// <param name="permission">Permission.</param> /// <returns>Object.</returns> public Permission Add(Permission permission) { if (permission == null) { throw new ArgumentNullException(nameof(permission)); } if (ExistsByName(permission.Name)) { throw new ArgumentException("An item with the same key has already been added."); } return(_ORM.Insert <Permission>(permission)); }
/// <summary> /// Add. /// </summary> /// <param name="role">Role.</param> /// <returns>Object.</returns> public Role Add(Role role) { if (role == null) { throw new ArgumentNullException(nameof(role)); } if (ExistsByName(role.Name)) { throw new ArgumentException("An item with the same key has already been added."); } return(_ORM.Insert <Role>(role)); }
/// <summary> /// Add. /// </summary> /// <param name="user">User.</param> /// <returns>Object.</returns> public User Add(User user) { if (user == null) { throw new ArgumentNullException(nameof(user)); } if (ExistsByName(user.Name)) { throw new ArgumentException("An item with the same key has already been added."); } return(_ORM.Insert <User>(user)); }
internal bool AddObject(Obj obj, Stream stream) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } if (String.IsNullOrEmpty(obj.GUID)) { obj.GUID = Guid.NewGuid().ToString(); } obj.BucketGUID = _Bucket.GUID; Obj test = GetObjectMetadata(obj.Key); if (test != null) { if (!_Bucket.EnableVersioning) { _Logging.Warn("BucketClient Add versioning disabled and object " + _Bucket.Name + "/" + obj.Key + " already exists"); return(false); } obj.Version = (test.Version + 1); } else { obj.Version = 1; } obj.Md5 = Common.BytesToHexString(_StorageDriver.Write(obj.BlobFilename, obj.ContentLength, stream)); if (String.IsNullOrEmpty(obj.Etag)) { obj.Etag = obj.Md5; } DateTime ts = DateTime.Now.ToUniversalTime(); obj.CreatedUtc = ts; obj.LastAccessUtc = ts; obj.LastUpdateUtc = ts; obj.ExpirationUtc = null; _ORM.Insert <Obj>(obj); return(true); }
/// <summary> /// Add an index. /// </summary> /// <param name="idx">Index.</param> /// <returns>Index.</returns> public KomodoIndex Add(Index idx) { if (idx == null) { throw new ArgumentNullException(nameof(idx)); } lock (_IndicesLock) { if (_Indices.Exists(i => i.Name.Equals(idx.Name))) { return(_Indices.First(i => i.Name.Equals(idx.Name))); } KomodoIndex ki = new KomodoIndex(_DbSettings, _SourceDocsStorageSettings, _ParsedDocsStorageSettings, idx); _ORM.Insert <Index>(idx); _Indices.Add(ki); return(ki); } }
private void RunSetup() { #region Variables DateTime timestamp = DateTime.Now; Settings settings = new Settings(); #endregion #region Welcome Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine(@" _ _ "); Console.WriteLine(@" | |____ ___ __| |__ __ _ ___ ___ "); Console.WriteLine(@" | / /\ V / '_ \ '_ \/ _` (_-</ -_) "); Console.WriteLine(@" |_\_\ \_/| .__/_.__/\__,_/__/\___| "); Console.WriteLine(@" |_| "); Console.WriteLine(@" "); Console.ResetColor(); Console.WriteLine(""); Console.WriteLine("Kvpbase Storage Server"); Console.WriteLine(""); // 1 2 3 4 5 6 7 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine("Thank you for using Kvpbase! We'll put together a basic system configuration"); Console.WriteLine("so you can be up and running quickly. You'll want to modify the System.json"); Console.WriteLine("file after to ensure a more secure operating environment."); #endregion #region Initial-Settings settings.EnableConsole = true; settings.Server = new Settings.SettingsServer(); settings.Server.Port = 8000; settings.Server.DnsHostname = "localhost"; settings.Server.Ssl = false; settings.Server.HeaderApiKey = "x-api-key"; settings.Server.MaxObjectSize = 2199023255552; // 2TB object size settings.Server.MaxTransferSize = 536870912; // 512MB transfer size settings.Storage = new Settings.SettingsStorage(); settings.Storage.Directory = "./Storage/"; Directory.CreateDirectory(settings.Storage.Directory); settings.Syslog = new Settings.SettingsSyslog(); settings.Syslog.ConsoleLogging = true; settings.Syslog.Header = "kvpbase"; settings.Syslog.ServerIp = "127.0.0.1"; settings.Syslog.ServerPort = 514; settings.Syslog.MinimumLevel = Severity.Info; settings.Syslog.FileLogging = true; settings.Syslog.LogDirectory = "./Logs/"; if (!Directory.Exists(settings.Syslog.LogDirectory)) { Directory.CreateDirectory(settings.Syslog.LogDirectory); } settings.Debug = new Settings.SettingsDebug(); settings.Debug.Database = false; settings.Debug.HttpRequest = false; #endregion #region Databases // 1 2 3 4 5 6 7 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine(""); Console.WriteLine("Kvpbase requires access to a database, either Sqlite, Microsoft SQL Server,"); Console.WriteLine("MySQL, or PostgreSQL. Please provide access details for your database. The"); Console.WriteLine("user account supplied must have the ability to CREATE and DROP tables along"); Console.WriteLine("with issue queries containing SELECT, INSERT, UPDATE, and DELETE. Setup will"); Console.WriteLine("attempt to create tables on your behalf if they dont exist."); Console.WriteLine(""); bool dbSet = false; while (!dbSet) { string userInput = Common.InputString("Database type [sqlite|sqlserver|mysql|postgresql]:", "sqlite", false); switch (userInput) { case "sqlite": settings.Database = new DatabaseSettings( Common.InputString("Filename:", "./Kvpbase.db", false) ); // 1 2 3 4 5 6 7 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine(""); Console.WriteLine("IMPORTANT: Using Sqlite in production is not recommended if deploying within a"); Console.WriteLine("containerized environment and the database file is stored within the container."); Console.WriteLine("Store the database file in external storage to ensure persistence."); Console.WriteLine(""); dbSet = true; break; case "sqlserver": settings.Database = new DatabaseSettings( Common.InputString("Hostname:", "localhost", false), Common.InputInteger("Port:", 1433, true, false), Common.InputString("Username:"******"sa", false), Common.InputString("Password:"******"Instance (for SQLEXPRESS):", null, true), Common.InputString("Database name:", "kvpbase", false) ); dbSet = true; break; case "mysql": settings.Database = new DatabaseSettings( DbTypes.Mysql, Common.InputString("Hostname:", "localhost", false), Common.InputInteger("Port:", 3306, true, false), Common.InputString("Username:"******"root", false), Common.InputString("Password:"******"Schema name:", "kvpbase", false) ); dbSet = true; break; case "postgresql": settings.Database = new DatabaseSettings( DbTypes.Postgresql, Common.InputString("Hostname:", "localhost", false), Common.InputInteger("Port:", 5432, true, false), Common.InputString("Username:"******"postgres", false), Common.InputString("Password:"******"Schema name:", "kvpbase", false) ); dbSet = true; break; } } #endregion #region Write-Files-and-Records Console.WriteLine(""); Console.WriteLine("| Writing system.json"); Common.WriteFile("System.json", Encoding.UTF8.GetBytes(Common.SerializeJson(settings, true))); Console.WriteLine("| Initializing logging"); LoggingModule logging = new LoggingModule("127.0.0.1", 514); logging.MinimumSeverity = Severity.Info; Console.WriteLine("| Initializing database"); WatsonORM orm = new WatsonORM(settings.Database); orm.InitializeDatabase(); orm.InitializeTable(typeof(ApiKey)); orm.InitializeTable(typeof(AuditLogEntry)); orm.InitializeTable(typeof(Container)); orm.InitializeTable(typeof(ContainerKeyValuePair)); orm.InitializeTable(typeof(ObjectKeyValuePair)); orm.InitializeTable(typeof(ObjectMetadata)); orm.InitializeTable(typeof(Permission)); orm.InitializeTable(typeof(UrlLock)); orm.InitializeTable(typeof(UserMaster)); Console.WriteLine("| Adding user [default]"); UserMaster user = new UserMaster(); user.GUID = "default"; user.Email = "*****@*****.**"; user.Password = "******"; user.FirstName = "Default"; user.LastName = "User"; user.CreatedUtc = timestamp; user.Active = true; user = orm.Insert <UserMaster>(user); Console.WriteLine("| Adding API key [default]"); ApiKey apiKey = new ApiKey(); apiKey = new ApiKey(); apiKey.GUID = "default"; apiKey.UserGUID = user.GUID; apiKey.Active = true; apiKey = orm.Insert <ApiKey>(apiKey); Console.WriteLine("| Adding permission [default]"); Permission perm = new Permission(); perm.GUID = Guid.NewGuid().ToString(); perm.UserGUID = user.GUID; perm.ContainerGUID = "default"; perm.DeleteContainer = true; perm.DeleteObject = true; perm.ReadContainer = true; perm.ReadObject = true; perm.WriteContainer = true; perm.WriteObject = true; perm.IsAdmin = true; perm.ApiKeyGUID = apiKey.GUID; perm.Notes = "Created by setup script"; perm.Active = true; perm = orm.Insert <Permission>(perm); Console.WriteLine("| Creating container [default]"); string htmlFile = SampleHtmlFile("http://github.com/kvpbase"); string textFile = SampleTextFile("http://github.com/kvpbase"); string jsonFile = SampleJsonFile("http://github.com/kvpbase"); ContainerManager containerMgr = new ContainerManager(settings, logging, orm); Container container = new Container(); container.UserGUID = "default"; container.Name = "default"; container.GUID = "default"; container.ObjectsDirectory = settings.Storage.Directory + container.UserGUID + "/" + container.Name + "/"; container.EnableAuditLogging = true; container.IsPublicRead = true; container.IsPublicWrite = false; containerMgr.Add(container); ContainerClient client = containerMgr.GetContainerClient("default", "default"); Console.WriteLine("| Writing sample files to container [default]"); ErrorCode error; client.WriteObject("hello.html", "text/html", Encoding.UTF8.GetBytes(htmlFile), null, out error); client.WriteObject("hello.txt", "text/plain", Encoding.UTF8.GetBytes(textFile), null, out error); client.WriteObject("hello.json", "application/json", Encoding.UTF8.GetBytes(jsonFile), null, out error); #endregion #region Wrap-Up // 1 2 3 4 5 6 7 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine(""); Console.WriteLine(Common.Line(79, "-")); Console.WriteLine(""); Console.WriteLine("We have created your first user account and permissions."); Console.WriteLine(""); ConsoleColor prior = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("IMPORTANT: The default API key setup creates has administrative privileges and"); Console.WriteLine("can use admin APIs. We recommend you modify your configuration and reduce its"); Console.WriteLine("permissions before exposing Kvpbase outside of localhost."); Console.ForegroundColor = prior; Console.WriteLine(""); Console.WriteLine(" API Key : " + apiKey.GUID); Console.WriteLine(""); Console.WriteLine("We've also created sample files for you so that you can see your node in"); Console.WriteLine("action. Go to the following URLs in your browser and see what happens!"); Console.WriteLine(""); Console.WriteLine(" http://localhost:8000/"); Console.WriteLine(" http://localhost:8000/default/default?_container&html"); Console.WriteLine(" http://localhost:8000/default/default/hello.html"); Console.WriteLine(" http://localhost:8000/default/default/hello.html?metadata"); Console.WriteLine(" http://localhost:8000/default/default/hello.txt"); Console.WriteLine(" http://localhost:8000/default/default/hello.json"); Console.WriteLine(""); #endregion }
static void Main(string[] args) { try { #region Setup Console.Write("DB type [sqlserver|mysql|postgresql|sqlite]: "); _DbType = Console.ReadLine(); if (String.IsNullOrEmpty(_DbType)) { return; } _DbType = _DbType.ToLower(); if (_DbType.Equals("sqlserver") || _DbType.Equals("mysql") || _DbType.Equals("postgresql")) { Console.Write("User: "******"Password: "******"sqlserver": _Settings = new DatabaseSettings(DbTypes.SqlServer, "localhost", 1433, _Username, _Password, "test"); break; case "mysql": _Settings = new DatabaseSettings(DbTypes.Mysql, "localhost", 3306, _Username, _Password, "test"); break; case "postgresql": _Settings = new DatabaseSettings(DbTypes.Postgresql, "localhost", 5432, _Username, _Password, "test"); break; default: return; } } else if (_DbType.Equals("sqlite")) { Console.Write("Filename: "); _Filename = Console.ReadLine(); if (String.IsNullOrEmpty(_Filename)) { return; } _Settings = new DatabaseSettings(_Filename); } else { Console.WriteLine("Invalid database type."); return; } _Orm = new WatsonORM(_Settings); _Orm.InitializeDatabase(); DebugSettings debug = new DebugSettings(); debug.DatabaseQueries = true; debug.DatabaseResults = true; _Orm.Logger = Logger; _Orm.Debug = debug; _Orm.InitializeTable(typeof(Person)); _Orm.TruncateTable(typeof(Person)); Console.WriteLine("Using table: " + _Orm.GetTableName(typeof(Person))); #endregion #region Insert-Records Person p1 = new Person("Abraham", "Lincoln", Convert.ToDateTime("1/1/1980"), null, 42, null, "initial notes p1", PersonType.Human, null, false); Person p2 = new Person("Ronald", "Reagan", Convert.ToDateTime("2/2/1981"), Convert.ToDateTime("3/3/1982"), 43, 43, "initial notes p2", PersonType.Cat, PersonType.Cat, true); Person p3 = new Person("George", "Bush", Convert.ToDateTime("3/3/1982"), null, 44, null, "initial notes p3", PersonType.Dog, PersonType.Dog, false); Person p4 = new Person("Barack", "Obama", Convert.ToDateTime("4/4/1983"), Convert.ToDateTime("5/5/1983"), 45, null, "initial notes p4", PersonType.Human, null, true); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p1"); p1 = _Orm.Insert <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p2"); p2 = _Orm.Insert <Person>(p2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p3"); p3 = _Orm.Insert <Person>(p3); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p4"); p4 = _Orm.Insert <Person>(p4); #endregion #region Insert-Multiple-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p5 through p8"); Person p5 = new Person("Jason", "Christner", Convert.ToDateTime("4/21/2020"), null, 1, null, "initial notes p5", PersonType.Human, null, false); Person p6 = new Person("Maria", "Sanchez", Convert.ToDateTime("10/10/1982"), Convert.ToDateTime("10/10/1982"), 38, null, "initial notes p6", PersonType.Cat, PersonType.Cat, true); Person p7 = new Person("Eddie", "Van Halen", Convert.ToDateTime("3/3/1982"), null, 44, null, "initial notes p7", PersonType.Dog, PersonType.Dog, false); Person p8 = new Person("Steve", "Vai", Convert.ToDateTime("4/4/1983"), Convert.ToDateTime("5/5/1983"), 45, null, "initial notes p8", PersonType.Human, null, true); List <Person> people = new List <Person> { p5, p6, p7, p8 }; _Orm.InsertMultiple <Person>(people); #endregion #region Exists-Count-Sum for (int i = 0; i < 8; i++) { Console.WriteLine(""); } DbExpression existsExpression = new DbExpression(_Orm.GetColumnName <Person>(nameof(Person.Id)), DbOperators.GreaterThan, 0); Console.WriteLine("| Checking existence of records: " + _Orm.Exists <Person>(existsExpression)); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } DbExpression countExpression = new DbExpression(_Orm.GetColumnName <Person>(nameof(Person.Id)), DbOperators.GreaterThan, 2); Console.WriteLine("| Checking count of records: " + _Orm.Count <Person>(countExpression)); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Checking sum of ages: " + _Orm.Sum <Person>(_Orm.GetColumnName <Person>(nameof(Person.Age)), existsExpression)); #endregion #region Select for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many by column name"); DbExpression eSelect1 = new DbExpression("id", DbOperators.GreaterThan, 0); List <Person> selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect1); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many by property name"); DbExpression eSelect2 = new DbExpression( _Orm.GetColumnName <Person>(nameof(Person.FirstName)), DbOperators.Equals, "Abraham"); List <Person> selectedList2 = _Orm.SelectMany <Person>(null, null, eSelect2); Console.WriteLine("| Retrieved: " + selectedList2.Count + " records"); foreach (Person curr in selectedList2) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by ID"); Person pSelected = _Orm.SelectByPrimaryKey <Person>(3); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting first by column name"); DbExpression eSelect3 = new DbExpression("id", DbOperators.Equals, 4); pSelected = _Orm.SelectFirst <Person>(eSelect3); Console.WriteLine("| Selected: " + pSelected.ToString()); #endregion #region Update-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p1"); p1.Notes = "updated notes p1"; p1 = _Orm.Update <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p2"); p2.Notes = "updated notes p2"; p2 = _Orm.Update <Person>(p2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p3"); p3.Notes = "updated notes p3"; p3 = _Orm.Update <Person>(p3); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p4"); p4.Notes = "updated notes p4"; p4 = _Orm.Update <Person>(p4); #endregion #region Update-Many-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating many records"); Dictionary <string, object> updateVals = new Dictionary <string, object>(); updateVals.Add(_Orm.GetColumnName <Person>("Notes"), "Updated during update many!"); _Orm.UpdateMany <Person>(eSelect1, updateVals); #endregion #region Select for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many, test 1"); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect1); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by ID"); pSelected = _Orm.SelectByPrimaryKey <Person>(3); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting first"); pSelected = _Orm.SelectFirst <Person>(eSelect2); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting between, test 1"); DbExpression eSelect4 = DbExpression.Between("id", new List <object> { 2, 4 }); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect4); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by persontype"); DbExpression eSelect5 = new DbExpression("persontype", DbOperators.Equals, PersonType.Dog); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect5); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting handsome people"); DbExpression eSelect6 = new DbExpression("ishandsome", DbOperators.Equals, true); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect6); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by reverse ID order"); DbExpression eSelect7 = new DbExpression("id", DbOperators.GreaterThan, 0); DbResultOrder[] resultOrder = new DbResultOrder[1]; resultOrder[0] = new DbResultOrder("id", DbOrderDirection.Descending); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect7, resultOrder); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } #endregion #region Exception for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Catching exception and displaying query"); try { _Orm.Query("SELECT * FROM person ((("); } catch (Exception e) { Console.WriteLine("Exception: " + e.Message); Console.WriteLine("Query : " + e.Data["Query"]); } #endregion #region Delete-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p1"); _Orm.Delete <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p2"); _Orm.DeleteByPrimaryKey <Person>(2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p3 and p4"); DbExpression eDelete = new DbExpression("id", DbOperators.GreaterThan, 2); _Orm.DeleteMany <Person>(eDelete); #endregion } catch (Exception e) { // Get stack trace for the exception with source file information var st = new StackTrace(e, true); // Get the top stack frame var frame = st.GetFrame(0); // Get the line number from the stack frame var line = frame.GetFileLineNumber(); Console.WriteLine("Stack trace:" + Environment.NewLine + SerializeJson(st, true)); Console.WriteLine("Stack frame: " + Environment.NewLine + SerializeJson(st, true)); Console.WriteLine("Line number: " + line); Console.WriteLine("Exception: " + Environment.NewLine + SerializeJson(e, true)); } Console.WriteLine(""); Console.WriteLine("Press ENTER to exit"); Console.ReadLine(); }
static void Main(string[] args) { try { #region Setup Console.Write("Filename: "); _Filename = Console.ReadLine(); if (String.IsNullOrEmpty(_Filename)) { return; } _Settings = new DatabaseSettings(_Filename); _Orm = new WatsonORM(_Settings); _Orm.InitializeDatabase(); DebugSettings debug = new DebugSettings(); debug.DatabaseQueries = true; debug.DatabaseResults = true; _Orm.Logger = Logger; _Orm.Debug = debug; _Orm.InitializeTable(typeof(Person)); _Orm.TruncateTable(typeof(Person)); Console.WriteLine("Using table: " + _Orm.GetTableName(typeof(Person))); #endregion #region Create-and-Store-Records DateTimeOffset localTime = new DateTimeOffset(Convert.ToDateTime("1/1/2021")); Person p1 = new Person("Abraham", "Lincoln", Convert.ToDateTime("1/1/1980"), null, localTime, null, 42, null, "initial notes p1", PersonType.Human, null, false); Person p2 = new Person("Ronald", "Reagan", Convert.ToDateTime("2/2/1981"), Convert.ToDateTime("3/3/1982"), localTime, localTime, 43, 43, "initial notes p2", PersonType.Cat, PersonType.Cat, true); Person p3 = new Person("George", "Bush", Convert.ToDateTime("3/3/1982"), null, localTime, null, 44, null, "initial notes p3", PersonType.Dog, PersonType.Dog, false); Person p4 = new Person("Barack", "Obama", Convert.ToDateTime("4/4/1983"), Convert.ToDateTime("5/5/1983"), localTime, localTime, 45, null, "initial notes p4", PersonType.Human, null, true); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p1"); p1 = _Orm.Insert <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p2"); p2 = _Orm.Insert <Person>(p2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p3"); p3 = _Orm.Insert <Person>(p3); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Creating p4"); p4 = _Orm.Insert <Person>(p4); #endregion #region Select for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many by column name"); DbExpression eSelect1 = new DbExpression("id", DbOperators.GreaterThan, 0); List <Person> selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect1); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many by property name"); DbExpression eSelect2 = new DbExpression( _Orm.GetColumnName <Person>(nameof(Person.FirstName)), DbOperators.Equals, "Abraham"); List <Person> selectedList2 = _Orm.SelectMany <Person>(null, null, eSelect2); Console.WriteLine("| Retrieved: " + selectedList2.Count + " records"); foreach (Person curr in selectedList2) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by ID"); Person pSelected = _Orm.SelectByPrimaryKey <Person>(3); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting first by column name"); DbExpression eSelect3 = new DbExpression("id", DbOperators.Equals, 4); pSelected = _Orm.SelectFirst <Person>(eSelect3); Console.WriteLine("| Selected: " + pSelected.ToString()); #endregion #region Update-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p1"); p1.Notes = "updated notes p1"; p1.NullableType = null; p1 = _Orm.Update <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p2"); p2.Notes = "updated notes p2"; p2.NullableType = null; p2 = _Orm.Update <Person>(p2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p3"); p3.Notes = "updated notes p3"; p3.NullableType = null; p3 = _Orm.Update <Person>(p3); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating p4"); p4.Notes = "updated notes p4"; p4.NullableType = null; p4 = _Orm.Update <Person>(p4); #endregion #region Update-Many-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Updating many records"); Dictionary <string, object> updateVals = new Dictionary <string, object>(); updateVals.Add(_Orm.GetColumnName <Person>("Notes"), "Updated during update many!"); _Orm.UpdateMany <Person>(eSelect1, updateVals); #endregion #region Select for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting many, test 1"); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect1); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by ID"); pSelected = _Orm.SelectByPrimaryKey <Person>(3); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting first"); pSelected = _Orm.SelectFirst <Person>(eSelect2); Console.WriteLine("| Selected: " + pSelected.ToString()); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting between, test 1"); DbExpression eSelect4 = DbExpression.Between("id", new List <object> { 2, 4 }); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect4); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by persontype"); DbExpression eSelect5 = new DbExpression("persontype", DbOperators.Equals, PersonType.Dog); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect5); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting handsome people"); DbExpression eSelect6 = new DbExpression("ishandsome", DbOperators.Equals, true); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect6); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Selecting by reverse ID order"); DbExpression eSelect7 = new DbExpression("id", DbOperators.GreaterThan, 0); DbResultOrder[] resultOrder = new DbResultOrder[1]; resultOrder[0] = new DbResultOrder("id", DbOrderDirection.Descending); selectedList1 = _Orm.SelectMany <Person>(null, null, eSelect7, resultOrder); Console.WriteLine("| Retrieved: " + selectedList1.Count + " records"); foreach (Person curr in selectedList1) { Console.WriteLine(" | " + curr.ToString()); } #endregion #region Exception for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Catching exception and displaying query"); try { _Orm.Query("SELECT * FROM person ((("); } catch (Exception e) { Console.WriteLine("Exception: " + e.Message); Console.WriteLine("Query : " + e.Data["Query"]); } #endregion #region Delete-Records for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p1"); _Orm.Delete <Person>(p1); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p2"); _Orm.DeleteByPrimaryKey <Person>(2); for (int i = 0; i < 8; i++) { Console.WriteLine(""); } Console.WriteLine("| Deleting p3 and p4"); DbExpression eDelete = new DbExpression("id", DbOperators.GreaterThan, 2); _Orm.DeleteMany <Person>(eDelete); #endregion } catch (Exception e) { // Get stack trace for the exception with source file information var st = new StackTrace(e, true); // Get the top stack frame var frame = st.GetFrame(0); // Get the line number from the stack frame var line = frame.GetFileLineNumber(); Console.WriteLine("Stack trace:" + Environment.NewLine + SerializeJson(st, true)); Console.WriteLine("Stack frame: " + Environment.NewLine + SerializeJson(st, true)); Console.WriteLine("Line number: " + line); Console.WriteLine("Exception: " + Environment.NewLine + SerializeJson(e, true)); } Console.WriteLine(""); Console.WriteLine("Press ENTER to exit"); Console.ReadLine(); }
private void RunSetup() { #region Variables DateTime timestamp = DateTime.UtcNow; Settings settings = new Settings(); #endregion #region Welcome Console.WriteLine( Environment.NewLine + Environment.NewLine + "oooo .o8 " + Environment.NewLine + "`888 888 " + Environment.NewLine + " 888 oooo .ooooo. ooo. .oo. .oo. .ooooo. .oooo888 .ooooo. " + Environment.NewLine + " 888 .8P' d88' `88b `888P'Y88bP'Y88b d88' `88b d88' `888 d88' `88b " + Environment.NewLine + " 888888. 888 888 888 888 888 888 888 888 888 888 888 " + Environment.NewLine + " 888 `88b. 888 888 888 888 888 888 888 888 888 888 888 " + Environment.NewLine + "o888o o888o `Y8bod8P' o888o o888o o888o `Y8bod8P' `Y8bod88P `Y8bod8P' " + Environment.NewLine + Environment.NewLine + Environment.NewLine); // ________________ 1 2 3 4 5 6 7 // ________________12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine("Thank you for using Komodo! We'll put together a basic system configuration"); Console.WriteLine("so you can be up and running quickly."); Console.WriteLine(""); Console.WriteLine(Common.Line(79, "-")); Console.WriteLine(""); #endregion #region Initial-Settings settings.EnableConsole = true; settings.Server = new Settings.ServerSettings(); settings.Server.HeaderApiKey = "x-api-key"; settings.Server.AdminApiKey = "komodoadmin"; settings.Server.ListenerPort = 9090; settings.Server.ListenerHostname = "localhost"; settings.Logging = new Settings.LoggingSettings(); settings.Logging.ConsoleLogging = true; settings.Logging.Header = "komodo"; settings.Logging.SyslogServerIp = "127.0.0.1"; settings.Logging.SyslogServerPort = 514; settings.Logging.MinimumLevel = Severity.Info; settings.Logging.FileLogging = true; settings.Logging.FileDirectory = "./logs/"; settings.Logging.Filename = "Komodo.log"; if (!Directory.Exists("./data/")) { Directory.CreateDirectory("./data/"); } if (!Directory.Exists("./logs/")) { Directory.CreateDirectory("./logs/"); } settings.Database = new DbSettings("./data/komodo.db"); string tempDirectory = "./data/temp/"; settings.TempStorage = new StorageSettings(new DiskSettings(tempDirectory)); if (!Directory.Exists(tempDirectory)) { Directory.CreateDirectory(tempDirectory); } string sourceDirectory = "./data/source/"; settings.SourceDocuments = new StorageSettings(new DiskSettings(sourceDirectory)); if (!Directory.Exists(sourceDirectory)) { Directory.CreateDirectory(sourceDirectory); } string parsedDirectory = "./data/parsed/"; settings.ParsedDocuments = new StorageSettings(new DiskSettings(parsedDirectory)); if (!Directory.Exists(parsedDirectory)) { Directory.CreateDirectory(parsedDirectory); } #endregion #region Initialize-Database-and-Create-Records WatsonORM orm = new WatsonORM(settings.Database.ToDatabaseSettings()); orm.InitializeDatabase(); orm.InitializeTable(typeof(ApiKey)); orm.InitializeTable(typeof(Index)); orm.InitializeTable(typeof(Metadata)); orm.InitializeTable(typeof(MetadataDocument)); orm.InitializeTable(typeof(Node)); orm.InitializeTable(typeof(ParsedDocument)); orm.InitializeTable(typeof(Permission)); orm.InitializeTable(typeof(SourceDocument)); orm.InitializeTable(typeof(TermDoc)); orm.InitializeTable(typeof(TermGuid)); orm.InitializeTable(typeof(User)); DbExpression e = new DbExpression("id", DbOperators.GreaterThan, 0); User user = null; ApiKey apiKey = null; Permission perm = null; Index idx = null; List <User> users = orm.SelectMany <User>(e); if (users == null || users.Count < 1) { Console.WriteLine("| Creating first user 'default'"); user = new User("default", "Default", "*****@*****.**", "default"); user = orm.Insert <User>(user); } else { Console.WriteLine("| Users already exist, not creating default user"); } List <Index> indices = orm.SelectMany <Index>(e); if (indices == null || indices.Count < 1) { Console.WriteLine("| Creating first index 'default'"); idx = new Index(user.GUID, "default"); idx = orm.Insert <Index>(idx); } else { Console.WriteLine("| Indices already exist, not creating default index"); } List <ApiKey> keys = orm.SelectMany <ApiKey>(e); if (keys == null || keys.Count < 1) { Console.WriteLine("| Creating first API key 'default'"); apiKey = new ApiKey("default", user.GUID, true); apiKey = orm.Insert <ApiKey>(apiKey); } else { Console.WriteLine("| API keys already exist, not creating default API key"); } List <Permission> perms = orm.SelectMany <Permission>(e); if (perms == null || perms.Count < 1) { Console.WriteLine("| Creating first permission 'default'"); perm = new Permission(idx.GUID, user.GUID, apiKey.GUID, true, true, true, true, true); perm = orm.Insert <Permission>(perm); } else { Console.WriteLine("| Permissions already exist, not creating default permissions"); } #endregion #region Write-System-JSON File.WriteAllBytes("./system.json", Encoding.UTF8.GetBytes(Common.SerializeJson(settings, true))); #endregion #region Wrap-Up string baseUrl = "http://localhost:" + settings.Server.ListenerPort; // ________________ 1 2 3 4 5 6 7 // ________________12345678901234567890123456789012345678901234567890123456789012345678901234567890 Console.WriteLine(""); Console.WriteLine("All finished!"); Console.WriteLine(""); Console.WriteLine("If you ever want to return to this setup wizard, just re-run the application"); Console.WriteLine("from the terminal with the 'setup' argument."); Console.WriteLine(""); Console.WriteLine("Verify Komodo is running in your browser using the following URL:"); Console.WriteLine(""); Console.WriteLine(" " + baseUrl); Console.WriteLine(""); Console.WriteLine("We've created your first index for you called 'First'. Try POSTing a JSON"); Console.WriteLine("document to the index using the API key 'default' using the URL:"); Console.WriteLine(""); Console.WriteLine(" " + baseUrl + "/default?type=json&name=My+First+Document&x-api-key=default"); Console.WriteLine(""); #endregion }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously private async Task AddDocumentToIndex(Document doc, List <string> tags) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { if (doc == null) { throw new ArgumentNullException(nameof(doc)); } string header = "[" + doc.GUID + "] "; Log(header + "beginning processing"); DateTime startTime = DateTime.Now; DateTime endTime = DateTime.Now; int termsRecorded = 0; try { #region Setup _CurrentThreads++; Dictionary <string, int> terms = new Dictionary <string, int>(); #endregion #region Process-Tags if (tags != null && tags.Count > 0) { Log(header + "processing tags"); foreach (string curr in tags) { if (String.IsNullOrEmpty(curr)) { continue; } if (!terms.ContainsKey(curr.ToLower())) { terms.Add(curr.ToLower(), 1); } else { int refcount = terms[curr.ToLower()]; refcount = refcount + 1; terms.Remove(curr.ToLower()); terms.Add(curr.ToLower(), refcount); } } } Log(header + "finished processing tags"); #endregion #region Process-Content Log(header + "processing content"); string content = Encoding.UTF8.GetString(doc.Data); string[] termsRaw = content.Split(_TermDelimiters, StringSplitOptions.RemoveEmptyEntries); List <string> termsAlphaOnly = new List <string>(); if (termsRaw != null && termsRaw.Length > 0) { foreach (string curr in termsRaw) { if (String.IsNullOrEmpty(curr)) { continue; } if (curr.Length < _TermMinimumLength) { continue; } if (_IgnoreWords.Contains(curr.ToLower())) { continue; } string currAlphaOnly = AlphaOnlyString(curr); if (String.IsNullOrEmpty(currAlphaOnly)) { continue; } termsAlphaOnly.Add(currAlphaOnly); } if (termsAlphaOnly != null && termsAlphaOnly.Count > 0) { foreach (string curr in termsAlphaOnly) { if (!terms.ContainsKey(curr.ToLower())) { terms.Add(curr.ToLower(), 1); } else { int refcount = terms[curr.ToLower()]; refcount = refcount + 1; terms.Remove(curr.ToLower()); terms.Add(curr.ToLower(), refcount); } } } Log(header + "extracted terms"); } else { Log(header + "no terms found"); } #endregion #region Remove-Existing-Entries DeleteDocumentByGuid(doc.GUID); Log(header + "deleting existing documents with GUID " + doc.GUID); DeleteDocumentByHandle(doc.Handle); Log(header + "deleting existing documents with handle " + doc.Handle); #endregion #region Create-New-Document-Entry doc = _ORM.Insert <Document>(doc); Log(header + "created document entry"); #endregion #region Create-New-Terms-Entries Log(header + "creating " + terms.Count + " index entries, please be patient"); foreach (KeyValuePair <string, int> term in terms) { IndexEntry entry = new IndexEntry(doc.GUID, term.Key, term.Value); entry = _ORM.Insert <IndexEntry>(entry); termsRecorded++; } #endregion return; } catch (TaskCanceledException) { Log(header + "cancellation requested"); } catch (OperationCanceledException) { Log(header + "cancellation requested"); } catch (Exception e) { Log(header + "exception encountered: " + Environment.NewLine + e.ToString()); throw; } finally { _CurrentThreads--; lock (_Lock) { if (_DocumentsIndexing != null && _DocumentsIndexing.Count > 0 && _DocumentsIndexing.Contains(doc.GUID)) { _DocumentsIndexing.Remove(doc.GUID); } } endTime = DateTime.Now; TimeSpan ts = (endTime - startTime); decimal msTotal = Convert.ToDecimal(ts.TotalMilliseconds.ToString("F")); decimal msPerTerm = 0; if (termsRecorded > 0) { msPerTerm = Convert.ToDecimal((msTotal / termsRecorded).ToString("F")); } Log(header + "finished; " + termsRecorded + " terms [" + msTotal + "ms total, " + msPerTerm + "ms/term]"); } }