mongodb的复杂查询

springboot集成mognoDB实现各种方式的查询

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

创建一个实体类

使用注解使得实体类对应数据库里面的一个集合,这里我抽取出来了一个公共的实体类

@Data
public class BaseMongoEntity implements Serializable {

    @ApiModelProperty(value = "id")
    @MongoId
    private ObjectId id;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    @ApiModelProperty(value = "更新时间")
    private Date updateTime;

    @ApiModelProperty(value = "逻辑删除(1:启用,0:删除)")
    private Integer status;

    @ApiModelProperty(value = "其他参数")
    @Transient //被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性
    private Map<String,Object> param = new HashMap<>();
}


@Data
@ApiModel(description = "article")
@Document("article")
public class ArticleEntity extends BaseMongoEntity {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "文章标题")
    @Field("title")
    private String title;

    @ApiModelProperty(value = "作者ID")
    @Field("author_id")
    private String authorId;

    @ApiModelProperty(value = "文章简介")
    @Field("summary")
    private String summary;

    @ApiModelProperty(value = "文章浏览数")
    @Field("view_counts")
    private Long viewCounts;

    @ApiModelProperty(value = "文章点赞数")
    @Field("star_counts")
    private Long starCounts;

    @ApiModelProperty(value = "文章评论数")
    @Field("comment_counts")
    private Long commentCounts;

    @ApiModelProperty(value = "文章专栏")
    @Field("category")
    private String category;

    @ApiModelProperty(value = "文章标签")
    @Field("tag_list")
    private List<String> tagList;

    @ApiModelProperty(value = "文章来源")
    @Field("source")
    private Integer source;

    @ApiModelProperty(value = "文章置顶")
    @Field("up")
    private Integer up;

    @ApiModelProperty(value = "文章精品")
    @Field("exquisite")
    private Integer exquisite;

    @ApiModelProperty(value = "文章内容")
    @Field("content")
    private String content;

    @ApiModelProperty(value = "文章内容html")
    @Field("content_html")
    private String contentHtml;

}

建立Dao层

注意,如果想要使用一些高级的操作,最好让我们的这个接口去继承 MongoRepository<ArticleEntity, ObjectId> ,这个泛型里面是两个参数,第一个是实体类的类名字,第二个是主键的类型

@Repository
public interface ArticleRepository extends MongoRepository<ArticleEntity, ObjectId> {

}
@NoRepositoryBean
public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
	@Override
	<S extends T> List<S> saveAll(Iterable<S> entities);

	@Override
	List<T> findAll();

	@Override
	List<T> findAll(Sort sort);

	<S extends T> S insert(S entity);

	<S extends T> List<S> insert(Iterable<S> entities);

	@Override
	<S extends T> List<S> findAll(Example<S> example);

	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

可以看到上面的这个父类有一些基本的方法, 这样写过之后,我们自己定义的ArticleRepository会默认实现一些基础的CRUD操作,在Spring容器中注入进去即可以使用。

高级功能

spring-data-mongo还是可以支持一些高级的操作的,比如他可以在我们自定义的dao层对象里面自定义方法,只要是可以被自动联想到的方法,我们将他写了出来都可以使用它

自动联想

当然,联想并不只是那么简单

/**
     * 查询文章总数
     *
     * @param id id
     * @return {@link Long}
     */
    Long countById(String id);


    /**
     * 找到所有由视图状态订单数量desc计数desc置评
     *
     * @param status   状态
     * @param pageable 可分页
     * @return {@link List<ArticleEntity>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleEntity> findAllByStatusOrderByViewCountsDescCommentCountsDesc(Integer status, Pageable pageable);

    /**
     * 根据文章内容进行左前缀模糊查询
     *
     * @param content 内容
     * @return {@link List<ArticleEntity>}
     */
    List<ArticleEntity> findAllByContentStartsWith(String content);

    /**
     * 根据作者id分页查询文章
     *
     * @param autherId 作者id
     * @param pageable 可分页
     * @return {@link IPage<ArticleEntity>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findAllByAuthorIdOrderByCreateTimeDesc(String autherId, Pageable pageable);

    /**
     * 根据作者id查询他的文章数量
     *
     * @param autherId 作者id
     * @return {@link Long}
     */
    Integer countByAuthorId(String autherId);

    /**
     * 根据标签进行查询文章
     * 置顶的放在最上面
     * 并且根据浏览量降序排序,
     *
     * @param tagName 标签名
     * @return {@link List<ArticleShowVo>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findAllByTagListOrderByUpDescViewCountsDesc(String tagName, Pageable pageable);

    /**
     * 查询某标签下的所有文章数量
     *
     * @param tagName 标签名
     * @return {@link Integer}
     */
    Integer countByTagList(String tagName);

    /**
     * 根据文章的标题进行左前缀匹配查询
     *
     * @param status   状态
     * @param title    标题
     * @param pageable 可分页
     * @return {@link List<ArticleShowVo>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findByStatusAndTitleStartsWithOrderByUpDescViewCountsDesc(Integer status, String title, Pageable pageable);


    /**
     * 按类别数和地位
     *
     * @param category 类别
     * @param status   状态
     * @return {@link Integer}
     */
    Integer countByCategoryAndStatus(String category,Integer status);

上述操作包含了排序、过滤、模糊查询、左前缀匹配查询、传入条件查询、将查询结果转换为另外一个对象、分页查询。下面整体的找一些比较有难度的来解答一下:

  • findAllByStatusOrderByViewCountsDescCommentCountsDesc 这个查询可以根据英文理解为 根据状态查询然后根据浏览量降序排序和评论量降序排序,那这个方法肯定需要一个参数,就是status,如果想要分页的话,就再传递一个Pageable类型的分页对象,最后的集合会根据查询条件分页的显示出来,不过并不是一个分页的对象,而是一个集合,我们并不知道它到底有多少条,所以也不知道到底有几页,这个就需要我们再次的封装了
  • @Query(fields = “{ ‘content’ : 0, ‘content_html’ : 0}”) 这个是在方法头上加的注解,它的含义就是我查询出来的内容不想要content和content_html这两个字段,相反,如果我们想要某几个字段,直接定义把0变成1就可以了,那样的话其他的就默认不显示出来了,
  • 如果我们不想要用原本的对象去接收这个查询的返回结果,那么也可以自己定义一个新的对象去接收,这个新的对象也要用注解进行标明,然后就会进行默认的自动装配,这样就避免了对象的一些属性查询到是null的尴尬情况,

附:

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.whaleal.community.model.article.ArticleEntity;
import com.whaleal.community.vo.article.ArticleShowVo;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 注释存储库
 *
 * @author: jy
 * @Date: 2021/07/05
 */
@Repository
public interface ArticleRepository extends MongoRepository<ArticleEntity, ObjectId> {


    /**
     * 查询文章总数
     *
     * @param id id
     * @return {@link Long}
     */
    Long countById(String id);


    /**
     * 找到所有由视图状态订单数量desc计数desc置评
     *
     * @param status   状态
     * @param pageable 可分页
     * @return {@link List<ArticleEntity>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleEntity> findAllByStatusOrderByViewCountsDescCommentCountsDesc(Integer status, Pageable pageable);

    /**
     * 根据文章内容进行左前缀模糊查询
     *
     * @param content 内容
     * @return {@link List<ArticleEntity>}
     */
    List<ArticleEntity> findAllByContentStartsWith(String content);

    /**
     * 根据作者id分页查询文章
     *
     * @param autherId 作者id
     * @param pageable 可分页
     * @return {@link IPage<ArticleEntity>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findAllByAuthorIdOrderByCreateTimeDesc(String autherId, Pageable pageable);

    /**
     * 根据作者id查询他的文章数量
     *
     * @param autherId 作者id
     * @return {@link Long}
     */
    Integer countByAuthorId(String autherId);

    /**
     * 根据标签进行查询文章
     * 置顶的放在最上面
     * 并且根据浏览量降序排序,
     *
     * @param tagName 标签名
     * @return {@link List<ArticleShowVo>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findAllByTagListOrderByUpDescViewCountsDesc(String tagName, Pageable pageable);

    /**
     * 查询某标签下的所有文章数量
     *
     * @param tagName 标签名
     * @return {@link Integer}
     */
    Integer countByTagList(String tagName);

    /**
     * 根据文章的标题进行左前缀匹配查询
     *
     * @param status   状态
     * @param title    标题
     * @param pageable 可分页
     * @return {@link List<ArticleShowVo>}
     */
    @Query(fields = "{ 'content' : 0, 'content_html' : 0}")
    List<ArticleShowVo> findByStatusAndTitleStartsWithOrderByUpDescViewCountsDesc(Integer status, String title, Pageable pageable);


    /**
     * 按类别数和地位
     *
     * @param category 类别
     * @param status   状态
     * @return {@link Integer}
     */
    Integer countByCategoryAndStatus(String category,Integer status);

}

@Data
public class ArticleShowVo {
    @ApiModelProperty(value = "id")
    @MongoId
    private ObjectId id;

    @ApiModelProperty(value = "文章标题")
    @Field("title")
    private String title;

    @ApiModelProperty(value = "作者ID")
    @Field("author_id")
    private String authorId;

    @ApiModelProperty(value = "文章简介")
    @Field("summary")
    private String summary;

    @ApiModelProperty(value = "文章浏览数")
    @Field("view_counts")
    private Long viewCounts;

    @ApiModelProperty(value = "文章点赞数")
    @Field("star_counts")
    private Long starCounts;

    @ApiModelProperty(value = "文章评论数")
    @Field("comment_counts")
    private Long commentCounts;

    @ApiModelProperty(value = "文章专栏")
    @Field("category")
    private String category;

    @ApiModelProperty(value = "文章标签")
    @Field("tag_list")
    private List<String> tagList;

    @ApiModelProperty(value = "文章来源")
    @Field("source")
    private Integer source;

    @ApiModelProperty(value = "文章置顶")
    @Field("up")
    private Integer up;

    @ApiModelProperty(value = "文章精品")
    @Field("exquisite")
    private Integer exquisite;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

}

自定义查询

最后,尽管目前这个CRUD操作已经很强大,但是在实际的场景中还是会有一些我们意想不到的需求,而我目前还没有找到关于MongoDB的那种类似于Mybatis一样可以直接写sql的框架,所以暂时就只能自己手写代码往query里面添加一些操作了,虽然感觉这样对于数据库可能很不友好,但是目前也只能如此了。下面举出一个例子
public Page<ArticleEntity> listArticle(Integer page, Integer limit, ArticleQueryVo articleQueryVo) {
        Pageable pageable = PageRequest.of(page - 1, limit);
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("title", ExampleMatcher.GenericPropertyMatchers.startsWith())
                .withIgnoreNullValues()
                .withIgnoreCase(true);
        //shopSetQueryVo转换Shop对象
        ArticleEntity article = new ArticleEntity();
        BeanUtils.copyProperties(articleQueryVo, article);
        Example<ArticleEntity> example = Example.of(article, matcher);

        DBObject dbObject = new BasicDBObject();

        BasicDBObject fieldsObject = new BasicDBObject();
        //指定返回的字段
        fieldsObject.put("content", false);
        fieldsObject.put("content_html", false);

        Query query = new BasicQuery(dbObject.toString(), fieldsObject.toString())
                .addCriteria(new Criteria().alike(example))
                .with(pageable);

        long count = mongoTemplate.count(query, ArticleEntity.class);
        if (count <= 0) {
            //没有查询到符合条件到结果
            return null;
        }
        mongoTemplate.find(query, ArticleEntity.class);
        //创建对象
        //调用方法实现查询,然后再将结果封装成为一个page对象
        List<ArticleEntity> entities = mongoTemplate.find(query, ArticleEntity.class);
        Page<ArticleEntity> pages = new PageImpl<>(entities, pageable, count);
        return pages;
    }

补充说明

这些自定义的查询因为需要具体的实现,而我们如果使用spring-data-mongo的框架来做的话,就是自己去拼凑这个Repository,然后自己写里面的方法,但是它是一个接口,里面并没有具体的实现,我们要是想写具体的实现的话,大部分的人都是在service层面去写,但是这样的话肯定会造成一些逻辑上的不清晰,基于这样的问题,我找到了一个解决的方法,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样就可以很好的解决这个问题了,一些简单的可以写的直接在继承了Repository的接口中去写,然后一些比较复杂的需要使用到MongoTemplate去操作的就在enhance里面去写。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值