spring mongo data api learn

1 索引

1.1 单列索引

@Indexed
@Field(value = "delete_flag")
private Boolean deleteFlag = false;

@Indexed属性:name定义索引名称、unique是否为唯一索引,默认false

1.2 组合索引

@Document(collection = "#{T(com.nd.social.common.handler.TenantHandler).getTablePrefix().concat('block_content')}")
@CompoundIndexes(
        @CompoundIndex(name = "idx_bc_t_sc_s", def = "{'tenant':1,'scope.code':1,'sourceId':1}", unique = true)
)
@TypeAlias("BlockContent")

@CompoundIndexes(
        @CompoundIndex(name = "_ui_s_df_idx_", def = "{'userId':1, 'status':1, 'deleteFlag':1}", unique = true)
)

 2 注意

在自定义接口实现中使用数据库中的字段名作为查询条件,而不是实体类的属性名

如果进行+1操作 尽量使用inc 避免并发问题

 

3 排序

    private static final Sort SORT_BY_CREATE_TIME_DESC =
            new Sort(Sort.Direction.DESC, "createAt");
    List<Idea> finByUserIdAndDeleteFlagFalse(String userId, Sort sort);
 命名查询:OrderBy...Desc/ASC
    List<Idea> findByUserIdAndStatusInAndViewAtLessThanAndDeleteFlagFalseOrderByCreateAtDesc(String userId, List<IdeaStatus> status, long viewAt);

 

4 分页

1 offset/limit

    private Pageable getPageable(SearchVo condition) {
        int limit = condition.getLimit();
        int offset = condition.getOffset();
        return new PageRequest(offset / limit, limit, SORT_BY_CREATE_TIME_DESC);
    }
    private int offset = 0;
    private int limit = 15;

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getLimit() {
        return limit <= 0 ? 15 : limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

2 page/size

page 页码,请求第几页数据(默认值:1) 可选
size 每页数量(默认值:30) 可选
new PageRequest(page - 1, size)

3 计算总页数

pageVo.setTotalPage(size == 0 ? 1 : (int) Math.ceil((double) total / (double) size));  

4 结果集示例

public class PageVo<T> {
    // 总数
    private long totalCount;
    // 总页数
    private int totalPage;
    // 页码
    private int page;
    // 单页数量
    private int size;
    // 结果列表
    private List<T> items;

    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        this.items = items;
    }

    public long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(long totalCount) {
        this.totalCount = totalCount;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}

 

public class Items<T> {

    // 结果列表 可以是Set/List
    private Collection<T> items;

    // 如果不需要 可以设置为items的size值
    private long totalCount;

    public static <T> Items<T> of(Collection<T> list) {
        Items<T> items = new Items<>();
        items.setItems(list);
        items.setTotalCount(list.size());
        return items;
    }

    public static <T> Items<T> of(Collection<T> list, long totalCount) {
        Items<T> items = new Items<>();
        items.setItems(list);
        items.setTotalCount(totalCount);
        return items;
    }

    public Collection<T> getItems() {
        return items;
    }

    public void setItems(Collection<T> items) {
        this.items = items;
    }

    public long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(long totalCount) {
        this.totalCount = totalCount;
    }

}

 

5 打印mongo NoSql语句

显示操作mongo的语句,log4j.properties里面加入:

1 log4j.logger.org.springframework.data.mongodb.core=DEBUG, mongodb
2 
3 log4j.appender.mongodb=org.apache.log4j.ConsoleAppender
4 log4j.appender.mongodb.Target=System.out
5 log4j.appender.mongodb.Threshold=DEBUG
6 log4j.appender.mongodb.ImmediateFlush=true
7 log4j.appender.mongodb.layout=org.apache.log4j.PatternLayout
8 log4j.appender.mongodb.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %X{RequestId} - %m%n

原因:
在mongo的底层实现中,如MongoTemplate中,判断了是否日志级别为debug,是的时候会打印语句出来,例如

 1 private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
 2 
 3 protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch,
 4             CursorPreparer preparer) {
 5 
 6         Assert.notNull(query);
 7 
 8         DBObject queryObject = queryMapper.getMappedObject(query.getQueryObject(), null);
 9         DBObject sortObject = query.getSortObject();
10         DBObject fieldsObject = query.getFieldsObject();
11 
12         if (LOGGER.isDebugEnabled()) {
13             LOGGER.debug("Executing query: {} sort: {} fields: {} in collection: {}", serializeToJsonSafely(queryObject),
14                     sortObject, fieldsObject, collectionName);
15         }
16 
17         this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
18 }

6 注解查询

1 一个方法命名查询中同一个属性不能出现2次 可以使用@Query注解查询
2 @Query:
  value 查询语句
  count 作为统计的查询 返回int值
  delete 作为删除语句并返回删除后的文档集合
  fields 限定需要返回哪些字段

示例:

@Query(count = true, value = "{'$and':[{'tenant':?3},{'reportStatus':?0}," +
            " {'dealTime':{'$gte':?1}}, {'dealTime':{'$lte':?2}}]}")
int countByStatusAndDealTimeBetween(ReportStatus status, Date begin, Date end, long tenant);

@Query("{'$and':[{'userId':?0},{'deleteFlag':false}," +
        "{'$or':[{'content':{'$regex':?1}},{'location':{'$regex':?1}},{'createAtStr':{'$regex':?1}}]}]}")
List<Idea> findByKeyWord(String userId, String key, Pageable pageable);
 1 {
 2     '$and': [
 3         {
 4             'userId': ?0
 5         },
 6         {
 7             'deleteFlag': false
 8         },
 9         {
10             '$or': [
11                 {
12                     'content': {
13                         '$regex': ?1
14                     }
15                 },
16                 {
17                     'location': {
18                         '$regex': ?1
19                     }
20                 },
21                 {
22                     'createAtStr': {
23                         '$regex': ?1
24                     }
25                 }
26             ]
27         }
28     ]
29 }

 

7 MongoOptions/MongoTemplate

public <T> T findOne(Query query, Class<T> entityClass) 
public boolean exists(Query query, Class<?> entityClass)
public <T> List<T> find(Query query, Class<T> entityClass)
public <T> T findById(Object id, Class<T> entityClass)

public <T> T findAndModify(Query query, Update update, Class<T> entityClass)
public <T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass)
public class FindAndModifyOptions {
    boolean returnNew;   // 是否返回更新后的值
    boolean upsert;      // 没有找到是否插入 
    boolean remove;      // 找到是否删除
}

public <T> T findAndRemove(Query query, Class<T> entityClass)
public long count(Query query, Class<?> entityClass) 

public void insert(Object objectToSave) 
public void insert(Collection<? extends Object> batchToSave, Class<?> entityClass)
public void insertAll(Collection<? extends Object> objectsToSave)

public void save(Object objectToSave) 保存/修改

public WriteResult upsert(Query query, Update update, Class<?> entityClass) 
public WriteResult updateFirst(Query query, Update update, Class<?> entityClass)
public WriteResult updateMulti(Query query, Update update, Class<?> entityClass)

public WriteResult remove(Object object)
public WriteResult remove(Query query, String collectionName)


public <T> List<T> findAll(Class<T> entityClass)
public <T> List<T> findAllAndRemove(Query query, Class<T> entityClass)

  public DB getDb()
  DBCollection getCollection(String collectionName); DBCollection 中包含各种CRUD操作以及对集合本身的定义操作(索引、命名)
  public String getCollectionName(Class<?> entityClass)

public <T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass)
public <T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,MapReduceOptions mapReduceOptions, Class<T> entityClass) 

public <T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass)


public <O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType)
public <O> AggregationResults<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType)

 

distinct方法:

 public List<String> distinctUserId() {
     return mongoTemplate.getCollection("ideas").distinct("user_id");
 }

 public List<String> distinctLocation(String userId) {
     DBObject query = Query.query(Criteria.where("user_id").is(userId)).getQueryObject();
     return mongoTemplate.getCollection("ideas").distinct("location", query);
 }

 

Sort

private final List<Order> orders;

public Sort and(Sort sort) {
  if (sort == null) {
    return this;
  }
  ArrayList<Order> these = new ArrayList<Order>(this.orders);
  for (Order order : sort) {
     these.add(order);
  }
  return new Sort(these);
}

Query

 1     private Sort sort;
 2     private int skip;
 3     private int limit;
 4 
 5     public Query skip(int skip) {
 6         this.skip = skip;
 7         return this;
 8     }
 9 
10     public Query limit(int limit) {
11         this.limit = limit;
12         return this;
13     }
14 
15         public Query with(Pageable pageable) {
16 
17         if (pageable == null) {
18             return this;
19         }
20 
21         this.limit = pageable.getPageSize();
22         this.skip = pageable.getOffset();
23 
24         return with(pageable.getSort());
25     }
26 
27     public Query with(Sort sort) {
28 
29         if (sort == null) {
30             return this;
31         }
32 
33         for (Order order : sort) {
34             if (order.isIgnoreCase()) {
35                 throw new IllegalArgumentException(String.format("Gven sort contained an Order for %s with ignore case! "
36                         + "MongoDB does not support sorting ignoreing case currently!", order.getProperty()));
37             }
38         }
39 
40         if (this.sort == null) {
41             this.sort = sort;
42         } else {
43             this.sort = this.sort.and(sort);
44         }
45 
46         return this;
47     }
48 
49 
50 
51 private final Map<String, CriteriaDefinition> criteria = new LinkedHashMap<String, CriteriaDefinition>();
52 
53     public static Query query(CriteriaDefinition criteriaDefinition) {
54         return new Query(criteriaDefinition);
55     }
56 
57     public Query() {}
58 
59 
60     public Query(CriteriaDefinition criteriaDefinition) {
61         addCriteria(criteriaDefinition);
62     }
63 
64 
65     public Query addCriteria(CriteriaDefinition criteriaDefinition) {
66 
67         CriteriaDefinition existing = this.criteria.get(criteriaDefinition.getKey());
68         String key = criteriaDefinition.getKey();
69 
70         if (existing == null) {
71             this.criteria.put(key, criteriaDefinition);
72         } else {
73             throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, "
74                     + "you can't add a second '" + key + "' criteria. " + "Query already contains '"
75                     + existing.getCriteriaObject() + "'.");
76         }
77 
78         return this;
79     }

Criteria

    private String key;
    private List<Criteria> criteriaChain;
    private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>();
    private Object isValue = NOT_SET;

    public static Criteria where(String key) {
        return new Criteria(key);
    }
public Criteria and(String key) { return new Criteria(this.criteriaChain, key); } is ne lt lte gt gte in/all nin mod size exits type not regex in: 包含其中一个即可 all:全部包含才可以 查询时要明确这多个值主键的关系是什么样的

public Criteria orOperator(Criteria... criteria)
public Criteria andOperator(Criteria... criteria)
public Criteria norOperator(Criteria... criteria)

8 案例

1 按照创建时间查找上一条下一条记录

 public Idea findIdeaNearTo(String userId, long createAt, boolean isNext) {
        Criteria criteria = Criteria.where("user_id").is(userId).and("delete_flag").is(false);
        Query query;
        if (isNext) {
            query = new Query(criteria).with(new Sort(Sort.Direction.ASC, "create_at"));
            criteria.and("create_at").gt(createAt);
        } else {
            query = new Query(criteria).with(new Sort(Sort.Direction.DESC, "create_at"));
            criteria.and("create_at").lt(createAt);
        }
        return mongoTemplate.findOne(query, Idea.class);
 }
next:
{ "user_id" : "2107164232" , "delete_flag" : false , "create_at" : { "$gt" : 1474600921000}}
pre:
{ "user_id" : "2107164232" , "delete_flag" : false , "create_at" : { "$lt" : 1474600921000}}

 

2 orOperator / andOperator

    public List<Idea> find(String userId, IdeaStatus status, OriginalityType type, long createAtFrom, long createAtTo) {
        Criteria criteria = Criteria.where("user_id").is(userId)
                .and("delete_flag").is(false)
                .and("status").in(status);
        if (type == null) {
            criteria.orOperator(
                    Criteria.where("originality_type").exists(false), // 字段是否存在 exists
                    Criteria.where("originality_type").size(0));      // 字段是数组,大小 size
        } else {
            criteria.and("originality_type").in(type);
        }
        criteria.andOperator(
                Criteria.where("create_at").gte(createAtFrom),
                Criteria.where("create_at").lt(createAtTo)
        );
        return mongoTemplate.find(new Query(criteria), Idea.class);
    } 
 1 {
 2     "user_id": "290536",
 3     "delete_flag": false,
 4     "status": {
 5         "$in": [
 6             "originality"
 7         ]
 8     },
 9     "$or": [
10         {
11             "originality_type": {
12                 "$exists": false
13             }
14         },
15         {
16             "originality_type": {
17                 "$size": 0
18             }
19         }
20     ],
21     "$and": [
22         {
23             "create_at": {
24                 "$gte": 1445788800000
25             }
26         },
27         {
28             "create_at": {
29                 "$lt": 1446393600000
30             }
31         }
32     ]
33 }

 注意:一个字段有多个条件限制,需要使用多个Criteria实现。各个Criteria之间使用orOperator或andOperator链接。

 案例2

 1  public Items<Idea> listOriginalities(String userId, SearchVo condition, Pageable pageable) {
 2         List<Criteria> orCriteriaList = new ArrayList<>();
 3         if (condition.getStatus() == null || condition.getStatus().size() == 0) {
 4             orCriteriaList.add(Criteria.where("status").in(Arrays.asList(IdeaStatus.originality)));
 5         } else {
 6             for (IdeaStatus status : condition.getStatus()) {
 7                 orCriteriaList.add(Criteria.where("status").all(Arrays.asList(status, IdeaStatus.originality)));
 8             }
 9         }
10         Criteria criteria = Criteria.where("userId").is(userId).and("deleteFlag").is(false)
11                 .orOperator(orCriteriaList.toArray(new Criteria[0]));
12 
13         if (!CollectionUtils.isEmpty(condition.getTag())) {
14             criteria.and("tags").in(condition.getTag());
15         }
16         Query query = query(criteria).with(pageable);
17         Query countQuery = query(criteria);
18         return Items.of(mongoTemplate.find(query, Idea.class), mongoTemplate.count(countQuery, Idea.class));
19     }
{
    "user_id": "2107164232",
    "delete_flag": false,
    "$or": [
        {
            "status": {
                "$all": [
                    "discussing",
                    "originality"
                ]
            }
        },
        {
            "status": {
                "$all": [
                    "considering",
                    "originality"
                ]
            }
        }
    ]
}

localhost:9088/v0.1/ideas/originality?tag=系统默认&status=discussing,considering

要求status必须是originality,若条件中包含status,必须是其中之一或全是。

public Criteria orOperator(Criteria... criteria) 

把条件中每一个状态拆分与originality组成一个查询条件,这样就得到一组查询条件,每个条件是or的关系。

3 更新或保存  更新返回旧值

 public BlockContent addIfNotExists(BlockContent blockContent, long tenant) {
        Query query = Query.query(where("tenant").is(tenant)
                .and("sourceId").is(blockContent.getSourceId())
                .and("scope.code").is(blockContent.getScope().getCode()));

        Update update = getUpdate(blockContent);

        return operations.findAndModify(
                query, update,
                options().upsert(true).returnNew(false),
                BlockContent.class
        );
    }
返回null 保存
返回旧值 更新 使用他的id

3 更新语句避免 n+1操作

    public void setBlockStatusAndRecoverTime(Set<String> ids, long tenant, Date recoverTime) {
        Query query = Query.query(
                where("_id").in(ids)
                        .and("tenant").is(tenant)
                        .and("blockStatus").is(BlockStatus.BLOCKED));

        operations.updateMulti(
                query,
                Update.update("blockStatus", BlockStatus.RECOVER)
                        .set("recoverTime", recoverTime),
                BlockContent.class
        );
    }

对于一样的修改动作,尤其更新少量字段时候(deleteFlag,dealTime,status),使用一条更新语句更新多个字段(updateMulti),而不是先查询,修改后保存

posted @ 2016-10-31 10:32  yweihainan  阅读(747)  评论(0编辑  收藏  举报