个人笔记谷粒商城项目第六节 ----(第122章~第140章)

第122章 分词器下载

一个 tokenizer (分词器)接收一个字符流,将之分割为独立的 tokens (词元,通常是独立
的单词),然后输出 tokens 流。例如,whitespace tokenizer 遇到空白字符时分割文本。它会将文本 " Quick brown fox! " 分割 为 [ Quick , brown , fox! ]。 tokenizer (分词器)还负责记录各个 term (词条)的顺序或 position 位置(用于 phrase 短 语和 word proximity 词近邻查询),以及 term (词条)所代表的原始 word (单词)的 start (起始)和 end (结束)的 character offsets (字符偏移量)(用于高亮显示搜索的内容)。
Elasticsearch 提供了很多内置的分词器,可以用来构建 custom analyzers (自定义分词器)。
https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v6.4.2 对应 es 版本安装
下载完成,放到进入 es 容器内部 plugins 目录即可。

第123章 修改liunx网络设置

我这里还是用的windows环境,等以后集群再上云服务安装环境,所以,这一章跳。

第124章 自定义词库

第125章 Elasticsearch-Rest-Client

使用后端整合ES,不能使用前端直接发送请求,因为不想对外暴露ES接口地址,为了安全。

引入流程 

<dependency>
   <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
     <version>7.4.2</version>
</dependency>

编写配置,给容器中注入一个RestHighLeveLCLient,GulimallElasticSearchConfig

@Configuration
public class GulimallElasticSearchConfig {

    // @Bean
    // public RestHighLevelClient esRestClient(){
    //     RestHighLevelClient client = new RestHighLevelClient(
    //             RestClient.builder(new HttpHost("192.168.137.14", 9200, "http")));
    //     return  client;
    // }

    public static final RequestOptions COMMON_OPTIONS;
    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        // builder.addHeader("Authorization", "Bearer " + TOKEN);
        // builder.setHttpAsyncResponseConsumerFactory(
        //         new HttpAsyncResponseConsumerFactory
        //                 .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    }

    @Bean
    public RestHighLevelClient esRestClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));
        return  client;
    }

}

第126集 测试保存

@Test
    public void indexData() throws IOException {

        IndexRequest indexRequest = new IndexRequest("users");
        indexRequest.id("1");   //数据的id

        // indexRequest.source("userName","zhangsan","age",18,"gender","男");

        User user = new User();
        user.setUserName("zhangsan");
        user.setAge(18);
        user.setGender("男");

        String jsonString = JSON.toJSONString(user);
        indexRequest.source(jsonString, XContentType.JSON);  //要保存的内容

        //执行操作
        IndexResponse index = client.index(indexRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

        //提取有用的响应数据
        System.out.println(index);

    }

    @Getter
    @Setter
    class User {
        private String userName;
        private String gender;
        private Integer age;
    }

第127章 测试复杂查询

这块最好自己动手写写,要不然容易忘记。

@Test
    public void searchState() throws IOException {
        //1. 创建检索请求
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //        sourceBuilder.query(QueryBuilders.termQuery("city", "Nicholson"));
        //        sourceBuilder.from(0);
        //        sourceBuilder.size(5);
        //        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("state", "AK");
        //                .fuzziness(Fuzziness.AUTO)
        //                .prefixLength(3)
        //                .maxExpansions(10);
        sourceBuilder.query(matchQueryBuilder);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank");
        searchRequest.source(sourceBuilder);
        //2. 执行检索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(searchResponse);

    }
@Test
    public void searchData() throws IOException {
        //1. 创建检索请求
        SearchRequest searchRequest = new SearchRequest();

        //1.1)指定索引
        searchRequest.indices("bank");
        //1.2)构造检索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery("address", "Mill"));


        //1.2.1)按照年龄分布进行聚合
        TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
        sourceBuilder.aggregation(ageAgg);

        //1.2.2)计算平均年龄
        AvgAggregationBuilder ageAvg = AggregationBuilders.avg("ageAvg").field("age");
        sourceBuilder.aggregation(ageAvg);
        //1.2.3)计算平均薪资
        AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
        sourceBuilder.aggregation(balanceAvg);

        System.out.println("检索条件:" + sourceBuilder);
        searchRequest.source(sourceBuilder);
        //2. 执行检索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println("检索结果:" + searchResponse);

        //3. 将检索结果封装为Bean
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit searchHit : searchHits) {
            String sourceAsString = searchHit.getSourceAsString();
            Account account = JSON.parseObject(sourceAsString, Account.class);
            System.out.println(account);

        }

        //4. 获取聚合信息
        Aggregations aggregations = searchResponse.getAggregations();

        Terms ageAgg1 = aggregations.get("ageAgg");

        for (Terms.Bucket bucket : ageAgg1.getBuckets()) {
            String keyAsString = bucket.getKeyAsString();
            System.out.println("年龄:" + keyAsString + " ==> " + bucket.getDocCount());
        }
        Avg ageAvg1 = aggregations.get("ageAvg");
        System.out.println("平均年龄:" + ageAvg1.getValue());

        Avg balanceAvg1 = aggregations.get("balanceAvg");
        System.out.println("平均薪资:" + balanceAvg1.getValue());
    }

第128章 ES在项目中的使用场景

ES承担项目内的所有全文检索功能,比如商品的检索功能,也负责日志的检索功能,日志的ELK使用的技术为Elasticsearch,LogStach,Kibana

ES的速度比mysql速度快很多,因为其是将数据存入内存之中, 那有人可能就要问了,商品数据那么大全部存内容中,可以吗?答:ES是一个天然支持分布式的架构,可以使用多个集群,保证内存的充足,将数据分片存储,即可解决这个问题。

之后使用ES要达成一个共识,就是内存要比硬盘存储要贵很多,所以只保存页面有用的数据,没用的我们都不要保存,如果要查一个商品的详细信息,我们可以先查ES获取sku,然后再查数据库,得到其他有用的信息。

商品的上架

上架的商品才可以在网站展示。 上架的商品需要可以被检索。

然后为啥ES的映射是spu结合sku的?
分析:商品上架在 es 中是存 sku 还是 spu
1 )、检索的时候输入名字,是需要按照 sku title 进行全文检索的
2 )、检索使用商品规格,规格是 spu 的公共属性,每个 spu 是一样的
3 )、按照分类 id 进去的都是直接列出 spu 的,还可以切换。
4 )、我们如果将 sku 的全量信息保存到 es 中(包括 spu 属性)就太多量字段了。
5 )、我们如果将 spu 以及他包含的 sku 信息保存到 es 中,也可以方便检索。但是 sku 属于
spu 的级联对象,在 es 中需要 nested 模型,这种性能差点。
6 )、但是存储与检索我们必须性能折中。
7 )、如果我们分拆存储, spu attr 一个索引, sku 单独一个索引可能涉及的 问题
检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,
再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就
10000*4=4MB ;并发情况下假设 1000 检索请求,那就是 4GB 的数据,,传输阻塞时间会很
长,业务更加无法继续。
所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数
据库范式。我们使用空间去换时间。
{
    "mappings": {
        "properties": {
            "skuId": {
                "type": "long"
            },
            "spuId": {
                "type": "keyword"
            },
            "skuTitle": {
                "type": "text",
                "analyzer": "ik_smart"
            },
            "skuPrice": {
                "type": "keyword"
            },
            "skuImg": {
                "type": "keyword",
                "index": false,
                "doc_values": false
            },
            "saleCount": {
                "type": "long"
            },
            "hasStock": {
                "type": "boolean"
            },
            "hotScore": {
                "type": "long"
            },
            "brandId": {
                "type": "long"
            },
            "catalogId": {
                "type": "long"
            },
            "brandName": {
                "type": "keyword",
                "index": false,
                "doc_values": false
            },
            "brandImg": {
                "type": "keyword",
                "index": false,
                "doc_values": false
            },
            "catalogName": {
                "type": "keyword",
                "index": false,
                "doc_values": false
            },
            "attrs": {
                "type": "nested",
                "properties": {
                    "attrId": {
                        "type": "long"
                    },
                    "attrName": {
                        "type": "keyword",
                        "index": false,
                        "doc_values": false
                    },
                    "attrValue": {
                        "type": "keyword"
                    }
                }
            }
        }
    }
}

第129集 nested数据场景的使用

我推荐一个博客 写的不错

nested给我的感觉就是,将两个东西绑定起来,只能捆绑查询。这一块还需要慢慢吸收

6.ELK之Elasticsearch嵌套(Nested)类型_elasticsearch nested-CSDN博客

第130集 商品上架,保存数据到ES中

这里我大概讲下业务逻辑吧。就是商品上架之后,发送请求传出spuid,想要将数据按照ES中设定的映射进行存储结构,所以,首先查出spuid对应的sku信息,查出商品的spu规格属性,同时过滤掉这些数据中的库存为空的数据。数据准备好后,然后发送请求给ES服务。

第131集

同上面集所讲

第132集 

远程调用的讲过好多次了,业务逻辑也比较简单,过。

第133集 发给ES进行保存

首先在ES服务中添加controller 

ElasticSaveController

@Slf4j
@RequestMapping(value = "/search/save")
@RestController
public class ElasticSaveController {

    @Autowired
    private ProductSaveService productSaveService;


    /**
     * 上架商品
     * @param skuEsModels
     * @return
     */
    @PostMapping(value = "/product")
    public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) {

        boolean status=false;
        try {
            status = productSaveService.productStatusUp(skuEsModels);
        } catch (IOException e) {
            //log.error("商品上架错误{}",e);

            return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
        }

        if(status){
            return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
        }else {
            return R.ok();
        }

    }


}

 ProductSaveServiceImpl,这个的含义就是调用esRestClient的API,类似于用postman发送请求一样,准备好json文件,准备好格式,准备好请求方式。获得错误的结果的返回信息。

@Override
    public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {

        //1.在es中建立索引,建立号映射关系(doc/json/product-mapping.json)

        //2. 在ES中保存这些数据
        BulkRequest bulkRequest = new BulkRequest();
        for (SkuEsModel skuEsModel : skuEsModels) {
            //构造保存请求
            IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
            indexRequest.id(skuEsModel.getSkuId().toString());
            String jsonString = JSON.toJSONString(skuEsModel);
            indexRequest.source(jsonString, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }


        BulkResponse bulk = esRestClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

        //TODO 如果批量错误
        boolean hasFailures = bulk.hasFailures();

        List<String> collect = Arrays.asList(bulk.getItems()).stream().map(item -> {
            return item.getId();
        }).collect(Collectors.toList());

        log.info("商品上架完成:{}",collect);

        return hasFailures;
    }

 第134集,第135集  调试代码以及feign源码讲解

这一节主要学习如何debug,以及feign源码讲解。我这一块也不太懂,等以后再补吧。QAQ

我这里有个报错,排查出没有加@Param("attrIds")导致的。

List<Long> selectSearchAttrIds(@Param("attrIds") List<Long> attrIds);

第136集 thymeleaf渲染首页

动静分离

静:图片,js、css等静态资源(以实际文件存在的方式)

动:服务器需要处理的请求,与相关业务相关的前端页面在微服务之中,保证每个微服务都可以独立运行,独立部署。

这个我遇见个问题,css没有加载出来

将所有的 href中的static都去掉,就变为这种 href="index/css/swiper-3.4.2.min.css"就可以显示了

之后改好就行了,前端内容过

第137集 整合dev-tool渲染一级分类

这一节主要讲前端的知识,后端配置简单,过

第138集 渲染二级三级分类数据

这一节也没什么新东西,过

第139集 Nginx配置域名访问环境

通过访问域名的方式访问网站。

我这里只用的windows环境 nginx.conf,可以成功跳转


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        location / {
             proxy_pass http://127.0.0.1:10001;
                }

        location /test {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

第140集 再修改下nginx配置

修改 nginx.conf文件,这里注意几个点吧,分号注意不要少,更新完配置之后一定要在任务管理器里关掉nginx,之后再点击nginx.exe,重启。然后注意如果重启后,任务管理器里面没有nginx,那说明你的nginx.conf 文件语法格式写错了


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    upstream gulimall{
        server 127.0.0.1:88;
    }

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        location / {
            proxy_set_header Host $host;
             proxy_pass http://gulimall;
                }

        location /test {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

然后网关的yml那个直接复制老师的就可以了。成功

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
R语言实战笔记第九介绍了方差分析的内容。方差分析是一种用于比较两个或多个组之间差异的统计方法。在R语言中,可以使用lm函数进行方差分析的回归拟合。lm函数的基本用法是: myfit <- lm(I(Y^(a))~x I(x^2) I(log(x)) var ... [-1],data=dataframe 其中,Y代表因变量,x代表自变量,a代表指数,var代表其他可能对模型有影响的变量。lm函数可以拟合回归模型并提供相关分析结果。 在方差分析中,还需要进行数据诊断,以确保模型的可靠性。其中几个重要的诊断包括异常观测值、离群点和高杠杆值点。异常观测值对于回归分析来说非常重要,可以通过Q-Q图和outlierTest函数来检测。离群点在Q-Q图中表示落在置信区间之外的点,需要删除后重新拟合并再次进行显著性检验。高杠杆值点是指在自变量因子空间中的离群点,可以通过帽子统计量来识别。一般来说,帽子统计量高于均值的2到3倍即可标记为高杠杆值点。 此外,方差分析还需要关注正态性。可以使用car包的qqplot函数绘制Q-Q图,并通过线的位置来判断数据是否服从正态分布。落在置信区间内为优,落在置信区间之外为异常点,需要进行处理。还可以通过绘制学生化残差的直方图和密度图来评估正态性。 综上所述,R语言实战第九介绍了方差分析及其相关的数据诊断方法,包括异常观测值、离群点、高杠杆值点和正态性检验。这些方法可以用于分析数据的可靠性和模型的适应性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [R语言实战笔记--第八 OLS回归分析](https://blog.csdn.net/gdyflxw/article/details/53870535)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值