基于Nodejs的express框架使用mogoose来操作mongoDB数据库实现分词搜索的精确查找的后端接口文档

Node后端目录结构

node后端的项目接口目录接口

sql/index.js

// 封装好的数据库的基本操作 增 删 改 查
const sql = {
  // 插入操纵
  insert (ColName, insertData) {//所需参数为:ColName:数据库中所需添加的集合名称以及规则,insertData:添加的数据
    console.log('11111111')
    return new Promise((resolve, reject) => {//异步函数需用到Promise
      ColName.insertMany(insertData, (err) => {//集合名.insertMany():插入数据,数据库中没有这个集合的时候就会自动创建
        err ? reject(err) : resolve()//出错的话返回错误信息,正确则返回执行成功
      })
    })
  },
  // 查询操作
  find (ColName, whereData, showData) {//所需参数:ColName:集合名称,whereData:查询条件(为空时就是全找),showData:字段名(即将所查到的信息中不要某个字段)
    return new Promise((resolve, reject) => {
      ColName.find(whereData, showData).exec((err, data) => {//.exec()为回调函数
        err ? reject(err) : resolve(data)
      })
    })
  },
  // 查询分页操作
  paging (ColName, whereData, showData, limit, count) {//参数:limit, count分别为页数和每页中所显示的数据条数
    return new Promise((resolve, reject) => {
      ColName.find(whereData, showData).limit(limit).skip(count * limit).exec((err, data) => {
        err ? reject(err) : resolve(data)
      })
    })
  },
  // 查询排序操作
  sort (ColName, whereData, showData, sortData) {
    return new Promise((resolve, reject) => {
      ColName.find(whereData, showData).sort(sortData).exec((err, data) => {
        err ? reject(err) : resolve(data)
      })
    })
  },
  // 更新数据库
  update (ColName, whereObj, updateObj, updateFlag) {
    // 如果updateFlag 为1 表示更新多条,其余为更新单条
    const updateType = updateFlag === 1 ? 'updateMany' : 'updateOne'
    // ColName[updateType] ===> updateFlag 1 ==> ColName.updateMany
    // ColName[updateType] ===> updateFlag 0 ==> ColName.updateOne
    return new Promise((resolve, reject) => {
      ColName[updateType](whereObj, updateObj, (err) => {
        err ? reject() : resolve()
      })
    })
  },
  // 封装一个删除数据库中数据的函数
  delete (ColName, whereObj, deleteFlag) {
    // 如果dalateFlag为 1 表示删除多件, 其余的为删除一件
    const deleteType = deleteFlag === 1 ? 'deleteMany' : 'deleteOne'
    return new Promise((resolve, reject) => {
      ColName[deleteType](whereObj, (err) => {
        err ? reject(err) : resolve()
      })
    })
  },
  // 分类的功能函数
  distinct (ColName, feild) {
    // 参数:ColName: 数据库中集合的名,feild: 需查找的字段名,
    // exec() 方法用于检索字符串中的正则表达式的匹配,返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
    // mongoose的distinct()方法,可以获取到数据中的指定字段的不重复值,例如 name: A, name: A, name: B, name: B 则使用distinct方法来查询以name为指定字段的值,则会返回 A B 而不会 A A B B,所以正好用于我们分类的查询,以type字段为依据来查找则只会返回不重复的type值,以数组的形式
    return new Promise((resovle, reject) => {
      ColName.distinct(feild).exec((err, data) => {
        err ? reject(err) : resovle(data)
      })
    })
  }
}

// 暴露出去在需要的地方引入
module.exports = sql

api/search.js

var express = require('express');
var router = express.Router();
var Segment = require('node-segment').Segment; // 载入node-segment中文分词模块
var Pro = require('./../sql/col/pros'); // 引入产品的集合即model层的东西
var sql = require('./../sql/index.js'); // 引入数据库操作模块

// 创建node-segment模块实列
var segment = new Segment();
// 使用默认的识别模块及字典
segment.useDefault();
/**
 * @api {get} api/search 分词-搜索功能接口
 * @apiDescription 分词-搜索功能接口
 * @apiGroup search
 * @apiParam { string } value 分词-搜索功能接口
 * @apiSuccessExample { json } Success-Response:
 * res.send({
    code: '200',
    message: '分词-搜索综合功能接口',
    data
  });
 * @apiSampleRequest /api/search
 * @apiVersion 1.0.0
 */
router.get('/',function(req, res, next) {
  // 1.node-segment中文分词模块对输入的字段进行分词关键词的抓取之类的
  let data = segment.doSegment(req.query.value)
  // res.send(data) // 分词后返回的是一个json类型的数组的形式
    /*[
        {
          "w": "万代",
          "p": 0
        },
        {
          "w": "南梦宫"
        }
      ]
    */
  // 2.对分词结果进行循环遍历并取出其中的关键词 即属性 w 的值,使用map()方法
  let KeyWordArr = data.map((item, index) => { return item.w })
  // 3.定义一个专门用来存放异步搜索结果的数组
  console.log(KeyWordArr)
  let SearchResult = []
  // 4.然后就可以进行数据库中搜索的操作了
  for(j = 0; j< KeyWordArr.length; j++){
    console.log(j)
    // 4.1将存放在KeyWordArr数组中的关键词循环遍历出来
    let value = KeyWordArr[j]
    console.log(value)
    // 4.2然后再将其一一编译为正则表达式
    let reg = new RegExp(value, 'i');
    // 4.3然后给这个正则表达式寻顶匹配的范围
    let searchObj = [
      { type: { $regex: reg } }, // 类型
      { city_name: { $regex: reg } }, // 举办城市
      { venue_name: { $regex: reg } }, // 剧院名称
      { name: { $regex: reg } }, // 剧名
      { show_time_top: { $regex: reg } }, // 时间 月日
      { show_time_bottom: { $regex: reg } }, // 时间 星期
      { start_show_time: { $regex: reg } }, // 开始时间
      { end_show_time: { $regex: reg } }, // 结束时间
      { support_desc: { $regex: reg } } // 电子票
    ]
    // 4.4最后就可以在数据库pro集合中搜索结果了(使用或搜所,只要匹配上就提取出来),并将搜索出来的结果保存在数组中.
    let Search = sql.find(Pro, { $or: searchObj }, { _id: 0 })
    console.log(Search)
    SearchResult.push(Search)
  }
  // 5.最后使用 Promise.all 等待所有的异步搜索都完成后向前端返回搜索到的值
  Promise.all(SearchResult).then(result => {
    // 5.1搜索出来的是一个二维数组所以需要扁平化一下
    let data = result.flat( Infinity )
    // 5.2因为分词搜索是对每一个被分出来关键词都进行一次数据库集合匹配,所以说有多少个关键词就会匹配多少次,而且这些匹配的结果中可能会出现与其他关键词相同的匹配结果或者说是压根就匹配不到结果,而且这些个结果被find出来后保存在一个数组中,然后每匹配完一个关键词我们就将这个数组保存在一个总的数组中,所以最后我们得出来的是一个二维的数组,这时候我们就需要对这个二维数组进行扁平化处理,而前边也说过了在这些搜索的结果中有可能会出现被重复匹配出来的结果,这时候扁平化后数组后就会重复元素,那么这时候我们可以统计一下每一个元素出现的次数,如果重复的次数越多就代表着搜索的结果就越精确,我们可以设计一个程序来根据重复次数的多到少来进行数组的排序并去重,这时候出现的出现的位置越靠前就代表着搜索的结果越精确
    let arr1 = []; // 用来复制data原数组
    let arr2 = []; // 用来存放每个下标位的元素的重复次数
    let arr3 = []; // 用来记录次数从大到小排序后的每个次数相对应的元素的下标位(因为是依次找出的最大值并提取出下标位push进来后再将arr2值赋值0后的arr3,可以当作是根据次数的从高到底排了一次序,不过记录的是每个元素的下标位而已)
    let num = 0; // 用于记录元素重复次数的
    // 5.2.1将data数组复制一份到arr1数组
    for(let i = 0;i < data.length; i++){
      arr1[i] = data[i]
    }
    // console.log(arr1)
    // 5.2.2记录每一个元素的重复次数
    for(let i = 0;i < data.length; i++){ // 嵌套循环data与arr1数组比较他们数组元素的id值(因为data与arr1数组完全相同,所以这样写这样就相当于每个元素都与其他元素进行了比对)
      for(let j = 0; j < arr1.length; j ++){
        if(data[i].proid === arr1[j].proid){ // 如果两个数组中的元素的id属性值相同的话就代表着数据相等,则次数加1
          num += 1
        }
      }
      arr2[i] = num // 并将此下标位元素的重复次数保存在数组arr2中(注意因为此处是相对应着下标位来将元素次数保存在数组中的,所以就可以根据这个次数的下标位来在data数组中取出相对应的元素)
      num = 0 // 保存完成之后将次数归零
    }
    // console.log('arr2',arr2)
    // 5.2.3然后我们可以获取到数组中的最大值然后记录下他的下标位,并将这个元素赋值为 0
    function fn(arr){
      let maxnum = Math.max(...arr) // 获取到arr数组中的最大值
      // console.log('maxnum',maxnum)
      let index = arr.indexOf(maxnum) // 在arr数组中寻找这个最大值
      arr3.push(index) //将这个arr数组最大值第一次出现的下标位记录在arr3数组中,因为是push进来的而且还是由大到小的可以当作是排了一次序(不一定是同一个data元素)
      arr[index] = 0 // 然后将这个下标位的元素赋值为0
      let newarr = []; // 定义一个新数组来接收更新元素后的新的数组,用于重新调用函数
      for(let j = 0;j < arr.length; j ++){ // 复制arr数组到数组newarr中
        newarr[j] = arr[j]
      }
      let condition = arr.every(function(item, index){
        return item === 0
      })
      if(condition){ // 出口
        return arr3
      } else { // 条件
        return fn(newarr)
      }
    }
    // console.log(arr3)
    // 5.2.4然后调用这个函数并传参,且接收返回值
    let arr4 = fn(arr2) // arr4用来接收函数处理后返回的arr3
    // console.log(arr4)
    let arr5 = []; // 用来保存根据从arr4中取出值的从原data数组中取到的值
    // 5.2.5然后根据这个数组中保存的下标位来从原数组中取值并去重后就是最后的数据
    for(let i = 0; i < arr4.length; i++){
      arr5.push(data[arr4[i]]) // arr4遍历出来的值就是元素中的下标位
    }
    // console.log(arr5)
    // 5.2.6给arr5数组进行去重
    let arr6 = quchong(arr5)
    function quchong(arr) {
      let array = [];
      for(let i = 0; i < arr.length; i++) {
        let isChage = true;
        for(let j = 0; j < array.length; j++) {
          if(arr[i]['proid'] == array[j]['proid']) {
              isChage = false;
          }
        }
        if(isChage) {
          array.push(arr[i]);
        }
      }
      return array;
    }
    res.send({
      code: '200',
      message: '搜索列表',
      len: arr6.length,
      data: arr6
    });
  })
})

module.exports = router;

搜索结果

搜索结果

发布了5 篇原创文章 · 获赞 10 · 访问量 287

猜你喜欢

转载自blog.csdn.net/weixin_46115723/article/details/105286666