zoukankan      html  css  js  c++  java
  • MongoDB在实际项目中的使用

    MongoDB简介

    MongoDB是近些年来流行起来的NoSql的代表,和传统数据库最大的区别是支持文档型数据库。
    当然,现在的一些数据库通过自定义复合类型,可变长数组等手段也可以模拟文档型数据库。
    例如在PostgreSQL中,以下是一个复合类型的例子

    CREATE TYPE complex AS (
        r       double precision,
        i       double precision
    );
    
    CREATE TYPE inventory_item AS (
        name            text,
        supplier_id     integer,
        price           numeric
    );
    

    数组的定义如下

    array[1,2]            --一维数组
    array[[1,2],[3,5]]  --二维数组
    

    当然,MongoDB生来就是文档型数据库,自然在应用层面对数据操作非常友好。

    • 使用了一套聚合框架来进行专业的聚合操作,和SQL语言相比,可以支持更加细致的操作
    • 可以使用JavaScript编写MapReduce函数进行数据统计操作,在分布式框架下适合处理大数据

    当然,你也可以将MongoDB当做普通的关系型数据库那样使用。但是这样就无法定义View(如果要使用View这样的功能,还是老老实实将MongoDB当做文档型数据库来使用吧)
    http://codesnippet.info/ 建站过程中,有些基础数据是简单的关系型数据,有些是缓存用文档型数据。

    MongoDB的管理工具

    这里我就推荐自己开发的工具MongoCola了。
    MongoCola项目Github地址
    这个项目从2011年到现在已经断断续续维持了5年了,从MongoDB1.0到MongoDB3.2,这个工具和MongoDB一起成长起来的。将近200个Star,最近又有两个两个朋友贡献了代码(现在使用C#开发Winform的人真的不多了),让我感到很欣慰。
    期间进行了一次比较大的重构(由于自己对于软件设计的理解的提高,以及从盲目的追求快速添加功能到追求整个项目代码的合理,才下决心进行了一次伤筋动骨的重构,当然现在再看,这次重构很重要,但是代码仍然还是有问题的,绝非完美。)
    在开发 www.codesnippet.info 的过程中,整个MONGODB数据库的查看都使用了这个工具,同时在使用中发现了一些BUG,也进行了一些改善。当然我现在也不敢保证BUG全部都清除干净了。如果发现BUG,请和我联系。
    原本打算使用Mono进行跨平台的,但是Mono对于Winform的支持并不好,所以虽然这个工具可以在Mac的OSX上运行,但是最好还是老老实实在Windows下运行比较好。发一张工具的界面图片,在OSX上截WIndows远程桌面的图。
    ![](http://codesnippet.info/FileSystem/Thumbnail?filename=00000001_20160501212726_屏幕快照 2016-05-01 下午9.24.42.png)

    C#驱动程序的再包装

    虽然官方的C#已经和不错了,虽然MongoDB原生支持ORM的。文档型对象就是OOP对象了。
    但是资深码农都会自己再写一些操作数据库的Helper方法。为自己定制的一套从EntityBase到ORM的方法。
    (关于时区的设定,其实可以在系列化设定中完成)

    using System;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace InfraStructure.DataBase
    {
        /// <summary>
        ///     实体
        ///     没有物理删除,所以自增SN是安全的
        /// </summary>
        public abstract class EntityBase
        {
            /// <summary>
            ///     创建时间
            /// </summary>
            [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateDateTime;
    
            /// <summary>
            ///     创建者
            ///     [这里可以是用户名,亦可是账号]
            /// </summary>
            public string CreateUser;
    
            /// <summary>
            ///     删除标记
            /// </summary>
            public bool IsDel;
    
            /// <summary>
            ///     序列号
            /// </summary>
            [BsonId] public string Sn;
    
            /// <summary>
            ///     更新时间
            /// </summary>
            [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime UpdateDateTime;
    
            /// <summary>
            ///     更新者
            /// </summary>
            public string UpdateUser;
    
            /// <summary>
            ///     获得表名称
            /// </summary>
            /// <returns></returns>
            public abstract string GetCollectionName();
    
            /// <summary>
            ///     获得主键前缀
            /// </summary>
            /// <returns></returns>
            public abstract string GetPrefix();
    
            /// <summary>
            /// 序列号格式
            /// </summary>
            public const string SnFormat = "D8";
    
        }
    }
    

    ORM的增删改也无非就是将驱动的数据库操作重新定制了一下而已。
    具体代码可以在本网站的开源项目中找到
    数据库操作辅助类

    MongoDB的序列化设定 (干货)

    • 场景一:由于类定义变更,某个字段不需要了,但是如果不进行设定的话,发序列化会出错,某个数据库字段没有配置的实体字段(IgnoreExtraElementsConvention)
    • 场景二:在序列化的时候,为了节省空间,希望字段为空的时候,不进行序列化。(IgnoreIfNullConvention)
    • 场景三:日期型的数据,序列化的时候,希望可以指定时区(RegisterSerializer)
                    //http://mongodb.github.io/mongo-csharp-driver/1.10/serialization/
                    var pack = new ConventionPack();
                    pack.Add(new IgnoreExtraElementsConvention(true));
                    pack.Add(new IgnoreIfNullConvention(true));
                    ConventionRegistry.Register("CustomElementsConvention", pack, t => { return true; });
                    //DateTime Localize    
                    BsonSerializer.RegisterSerializer(typeof(DateTime), new DateTimeSerializer(DateTimeKind.Local));
                    return true;
    

    当然,你也可以对某个日期型字段单独指定时区,或者将某个字段定义为主键。详细请参考上文提到的 [BsonId] 和 [BsonDateTimeOptions(Kind = DateTimeKind.Local)] 特性。

    FileStorage

    MongoDB虽然可以使用FileSystem,但是由于是内存型数据库,可能会大量消耗内存资源。

    这里贴一下FileSystemHelper,但是不建议大型项目在没有足够资源的情况下使用!!

    using System;
    using System.Collections.Generic;
    using System.Web;
    using MongoDB.Driver.GridFS;
    using System.IO;
    using MongoDB.Driver;
    using System.Drawing.Imaging;
    using System.Drawing;
    
    namespace InfraStructure.Storage
    {
        public static class MongoStorage
        {
            /// <summary>
            ///     服务器
            /// </summary>
            private static MongoServer _innerServer;
            /// <summary>
            ///     链接字符串
            /// </summary>
            private static readonly string Connectionstring = @"mongodb://localhost:";
    
            /// <summary>
            ///     初始化MongoDB
            /// </summary>
            /// <param name="dbList">除去Logger以外</param>
            /// <param name="defaultDbName"></param>
            /// <param name="port"></param>
            /// <returns></returns>
            public static bool Init(string port = "28030")
            {
                try
                {
                    _innerServer = new MongoClient(Connectionstring + port).GetServer();
                    _innerServer.Connect();
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
            /// <summary>
            ///     保存文件
            /// </summary>
            /// <param name="file"></param>
            /// <param name="ownerId"></param>
            /// <param name="databaseType"></param>
            /// <returns></returns>
            public static string InsertFile(HttpPostedFileBase file, string ownerId, string databaseType)
            {
                var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + file.FileName;
                var innerFileServer = _innerServer.GetDatabase(databaseType);
                var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
                gfs.Upload(file.InputStream, mongofilename);
                return mongofilename;
            }
            /// <summary>
            ///     保存文件
            /// </summary>
            /// <param name="file"></param>
            /// <param name="ownerId"></param>
            /// <param name="databaseType"></param>
            /// <returns></returns>
            public static string InsertFile(HttpPostedFile file, string ownerId, string databaseType)
            {
                return InsertFile(new HttpPostedFileWrapper(file) as HttpPostedFileBase, ownerId, databaseType);
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="file"></param>
            /// <param name="fileName"></param>
            /// <param name="ownerId"></param>
            /// <param name="databaseType"></param>
            /// <returns></returns>
            public static string InsertFile(Stream file, string fileName, string ownerId, string databaseType)
            {
                var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + fileName;
                var innerFileServer = _innerServer.GetDatabase(databaseType);
                var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
                gfs.Upload(file, mongofilename);
                return mongofilename;
            }
            /// <summary>
            /// 保存流为固定文件名
            /// </summary>
            /// <param name="file"></param>
            /// <param name="fixedFilename"></param>
            /// <param name="databaseType"></param>
            public static void InsertStreamWithFixFileName(Stream file, string fixedFilename, string databaseType)
            {
                var innerFileServer = _innerServer.GetDatabase(databaseType);
                var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
                gfs.Upload(file, fixedFilename);
            }
    
    
            /// <summary>
            ///     获得文件
            /// </summary>
            /// <param name="stream"></param>
            /// <param name="filename"></param>
            public static void GetFile(Stream stream, string filename, string databaseType)
            {
                var innerFileServer = _innerServer.GetDatabase(databaseType);
                var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
                if (gfs.Exists(filename))
                {
                    gfs.Download(stream, filename);
                }
            }
            /// <summary>
            ///     用户上传图片
            /// </summary>
            /// <param name="file"></param>
            /// <param name="ownerId"></param>
            /// <param name="weight"></param>
            /// <param name="height"></param>
            /// <returns></returns>
            public static string InsertImage(HttpPostedFileBase file, string ownerId, int weight = 64, int height = 64)
            {
                var fileName = file.FileName;
                var originalImage = Image.FromStream(file.InputStream);
                var thumbImage = originalImage.GetThumbnailImage(weight, height, null, IntPtr.Zero);
                using (var ms = new MemoryStream())
                {
                    thumbImage.Save(ms, ImageFormat.Jpeg);
                    //必须将位置重置
                    ms.Position = 0;
                    fileName = InsertFile(ms, fileName, ownerId, string.Empty);
                }
                return fileName;
            }
            /// <summary>
            /// Mongo文件结构
            /// </summary>
            public struct DbFileInfo
            {
                /// <summary>
                /// 文件名
                /// </summary>
                public string FileName;
                /// <summary>
                /// 数据库文件名
                /// </summary>
                public string DbFileName;
            }
            /// <summary>
            /// 文件备份
            /// </summary>
            /// <param name="fileList">文件列表</param>
            /// <param name="path">备份路径。注意以斜线结尾</param>
            /// <param name="databaseType">数据库名称</param>
            public static void BackUpFiles(List<DbFileInfo> fileList, string path, string databaseType)
            {
                var innerFileServer = _innerServer.GetDatabase(databaseType);
                foreach (var item in fileList)
                {
                    var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
                    gfs.Download(path + item.FileName, item.DbFileName);
                }
            }
        }
    }
    
    

    全文检索

    如果您的项目是英文项目,可以不需要第三方库,直接在MongoDB中完成全文检索。前提条件是为数据集设定文本索引
    如果您的项目是中文项目,必须使用企业版的MongoDB,加上第三方的分词库,才能实现全文检索
    http://codesnippet.info/ 使用的是ElasticSearch进行全文检索的。
    使用NEST操作ElasticSearch进行全文检索
    全文检索的索引设定和使用:

            /// <summary>
            ///     设置Text索引
            /// </summary>
            /// <param name="collectionName"></param>
            /// <param name="FieldName"></param>
            /// <param name="database"></param>
            public static void SetTextIndex(string collectionName, string FieldName, string database = "")
            {
                if (string.IsNullOrEmpty(database)) database = _defaultDatabaseName;
                MongoCollection col = GetDatabaseByType(database).GetCollection(collectionName);
                if (col.IndexExistsByName(FieldName))
                {
                    return;
                }
                var option = new IndexOptionsBuilder();
                option.SetName(FieldName);
                var indexkeys = new IndexKeysBuilder();
                indexkeys.Text(new string[] { FieldName });
                col.CreateIndex(indexkeys, option);
            }
    
            /// <summary>
            /// 全文检索
            /// </summary>
            /// <param name="collectionName"></param>
            /// <param name="key"></param>
            /// <param name="caseSensitive"></param>
            /// <param name="limit"></param>
            /// <returns></returns>
            public static List<BsonDocument> SearchText(string collectionName, string key, bool caseSensitive, int limit, IMongoQuery query = null)
            {
                //检索关键字
                var textSearchOption = new TextSearchOptions();
                textSearchOption.CaseSensitive = caseSensitive;
                var textSearchQuery = Query.Text(key, textSearchOption);
                if (query != null)
                {
                    textSearchQuery = Query.And(textSearchQuery, query);
                }
                MongoCollection col = GetDatabaseByType(_defaultDatabaseName).GetCollection(collectionName);
                var result = col.FindAs<BsonDocument>(textSearchQuery);
                var resultDocumentList = result.SetLimit(limit).ToList();
                return resultDocumentList;
            }
    

    内置的聚合操作

    MongoDB提供了一些内置的聚合函数,通过驱动程序可以直接使用

            /// <summary>
            /// Distinct
            /// </summary>
            /// <param name="collectionName"></param>
            /// <param name="FieldName"></param>
            /// <param name="query"></param>
            /// <returns></returns>
            public static List<string> Distinct(string collectionName, string FieldName, IMongoQuery query = null)
            {
                MongoCollection col = GetDatabaseByType(_defaultDatabaseName).GetCollection(collectionName);
                var DistinctResult = col.Distinct(FieldName, query);
                var result = new List<string>();
                foreach (BsonValue item in DistinctResult)
                {
                    result.Add(item.AsString);
                }
                return result;
            }
    

    使用Javascript进行聚合操作

    MongoDB是可以使用js进行聚合操作的。由于MongoDB内置了Google的V8引擎,所以可以通过运行自定义的Js片段来进行一些聚合操作。

          /// <summary>
            /// GroupByCount
            /// </summary>
            /// <param name="collectionName"></param>
            /// <param name="FieldName"></param>
            /// <returns></returns>
            public static Dictionary<string, int> GroupCount(string collectionName, string FieldName, IMongoQuery query = null)
            {
                MongoCollection col = GetDatabaseByType(_defaultDatabaseName).GetCollection(collectionName);
                GroupArgs g = new GroupArgs();
                var groupdoc = new GroupByDocument();
                groupdoc.Add(FieldName, true);
                g.KeyFields = groupdoc;
                g.ReduceFunction = new BsonJavaScript("function(obj,prev){ prev.count++;}");
                g.Initial = new BsonDocument().Add("count", 0);
                if (query != null)
                {
                    g.Query = query;
                }
                var GroupResult = col.Group(g);
                var result = new Dictionary<string, int>();
                foreach (BsonDocument item in GroupResult)
                {
                    result.Add(item.GetElement(FieldName).Value.ToString(), (int)item.GetElement("count").Value.AsDouble);
                }
                return result;
            }
    

    TTL索引

    使用MongoDB的TTL(TimeToLive)索引,可以实现定时缓存功能,数据经过指定时间后就自动从数据库里面删除。
    很多缓存数据现在就是用TTL索引实现15分钟自动清除操作的。

            /// <summary>
            ///     设定数据缓存时间(以创建时间为基础)
            /// </summary>
            /// <param name="collectionName"></param>
            /// <param name="ExpiresMinute"></param>
            /// <param name="database"></param>
            public static void SetCacheTime(string collectionName, int ExpiresMinute, string database = "")
            {
                if (string.IsNullOrEmpty(database)) database = _defaultDatabaseName;
                MongoCollection col = GetDatabaseByType(database).GetCollection(collectionName);
                if (col.IndexExistsByName("Cache"))
                {
                    col.DropIndexByName("Cache");
                }
                var option = new IndexOptionsBuilder();
                option.SetTimeToLive(new TimeSpan(0, ExpiresMinute, 0));
                option.SetName("Cache");
                var indexkeys = new IndexKeysBuilder();
                indexkeys.Ascending(new string[] { nameof(EntityBase.CreateDateTime) });
                col.CreateIndex(indexkeys, option);
            }
    
  • 相关阅读:
    ajax专题
    luogu P1346 电车 最短路
    luogu P1462 通往奥格瑞玛的道路 最短路
    luogu P1328 生活大爆炸版石头剪刀布
    luogu P1315 联合权值 枚举
    luogu P1156 垃圾陷阱 背包问题
    luogu P1217 回文质数 枚举
    luogu P3650 滑雪课程设计 枚举
    luogu1209 修理牛棚 贪心
    luogu P1223 排队接水 贪心
  • 原文地址:https://www.cnblogs.com/TextEditor/p/5454168.html
Copyright © 2011-2022 走看看