8

SpringDataとMongoDBを使った最初の実験は素晴らしかった。これで、次の構造(簡略化)が得られました。

public class Letter {
  @Id
  private String id;
  private List<Section> sections;
}

public class Section {
  private String id;
  private String content;
}

Letterオブジェクト/ドキュメント全体のロードと保存は魅力のように機能します。(ObjectIdを使用して、Section.idフィールドの一意のIDを生成します。)

Letter letter1 = mongoTemplate.findById(id, Letter.class)
mongoTemplate.insert(letter2);
mongoTemplate.save(letter3);

ドキュメントは大きく(200K)、アプリケーションで必要なのはサブパーツのみである場合があります。サブドキュメント(セクション)を照会し、変更して保存する可能性はありますか?次のようなメソッドを実装したい

Section s = findLetterSection(letterId, sectionId);
s.setText("blubb");
replaceLetterSection(letterId, sectionId, s);

そしてもちろん、次のような方法もあります。

addLetterSection(letterId, s); // add after last section
insertLetterSection(letterId, sectionId, s); // insert before given section
deleteLetterSection(letterId, sectionId); // delete given section

最後の3つの方法はやや「奇妙」であることがわかります。つまり、ドキュメント全体をロードし、コレクションを変更して再度保存する方が、オブジェクト指向の観点からはより良いアプローチかもしれません。しかし、最初のユースケース(サブドキュメント/サブオブジェクトに「ナビゲート」し、このオブジェクトのスコープで作業する)は自然なようです。

MongoDBはサブドキュメントを更新できると思いますが、SpringDataをオブジェクトマッピングに使用できますか?ポインタをありがとう。

4

3 に答える 3

18

1つのサブオブジェクトのみをスライスしてロードするための次のアプローチを考え出しました。大丈夫ですか?同時変更の問題を認識しています。

Query query1 = Query.query(Criteria.where("_id").is(instance));
query1.fields().include("sections._id");
LetterInstance letter1 = mongoTemplate.findOne(query1, LetterInstance.class); 
LetterSection emptySection = letter1.findSectionById(sectionId);
int index = letter1.getSections().indexOf(emptySection);

Query query2 = Query.query(Criteria.where("_id").is(instance));
query2.fields().include("sections").slice("sections", index, 1);
LetterInstance letter2 = mongoTemplate.findOne(query2, LetterInstance.class);
LetterSection section = letter2.getSections().get(0);

これは、すべてのセクションをロードするが、他の(大きな)フィールドを省略した代替ソリューションです。

Query query = Query.query(Criteria.where("_id").is(instance));
query.fields().include("sections");
LetterInstance letter = mongoTemplate.findOne(query, LetterInstance.class); 
LetterSection section = letter.findSectionById(sectionId);

これは、単一のコレクション要素のみを格納するために使用するコードです。

MongoConverter converter = mongoTemplate.getConverter();
DBObject newSectionRec = (DBObject)converter.convertToMongoType(newSection);

Query query = Query.query(Criteria.where("_id").is(instance).and("sections._id").is(new ObjectId(newSection.getSectionId())));
Update update = new Update().set("sections.$", newSectionRec);
mongoTemplate.updateFirst(query, update, LetterInstance.class);

SpringDataをMongoDBの「部分的な結果」でどのように使用できるかを見るのは素晴らしいことです。

コメントは大歓迎です!

于 2012-08-21T12:19:53.853 に答える
2

Matthias Wuttkeの答えは素晴らしいと思います。彼の答えの一般的なバージョンを探している人は、以下のコードを参照してください。

@Service
public class MongoUtils {

  @Autowired
  private MongoTemplate mongo;

  public <D, N extends Domain> N findNestedDocument(Class<D> docClass, String collectionName, UUID outerId, UUID innerId, 
    Function<D, List<N>> collectionGetter) {
    // get index of subdocument in array
    Query query = new Query(Criteria.where("_id").is(outerId).and(collectionName + "._id").is(innerId));
    query.fields().include(collectionName + "._id");
    D obj = mongo.findOne(query, docClass);
    if (obj == null) {
      return null;
    }
    List<UUID> itemIds = collectionGetter.apply(obj).stream().map(N::getId).collect(Collectors.toList());
    int index = itemIds.indexOf(innerId);
    if (index == -1) {
      return null;
    }

    // retrieve subdocument at index using slice operator
    Query query2 = new Query(Criteria.where("_id").is(outerId).and(collectionName + "._id").is(innerId));
    query2.fields().include(collectionName).slice(collectionName, index, 1);
    D obj2 = mongo.findOne(query2, docClass);
    if (obj2 == null) {
      return null;
    }
    return collectionGetter.apply(obj2).get(0);
  }

  public void removeNestedDocument(UUID outerId, UUID innerId, String collectionName, Class<?> outerClass) {
    Update update = new Update();
    update.pull(collectionName, new Query(Criteria.where("_id").is(innerId)));
    mongo.updateFirst(new Query(Criteria.where("_id").is(outerId)), update, outerClass);
  }
}

これは、たとえば、を使用して呼び出すことができます

mongoUtils.findNestedDocument(Shop.class, "items", shopId, itemId, Shop::getItems);
mongoUtils.removeNestedDocument(shopId, itemId, "items", Shop.class);

インターフェイスは次のDomainようになります。

public interface Domain {

  UUID getId();
}

注意:ネストされたドキュメントのコンストラクターにプリミティブデータ型の要素が含まれている場合、クラスをnull引数でインスタンス化できるようにするには、ネストされたドキュメントにデフォルトの(空の)コンストラクターがあり、保護されている可能性があります。

于 2018-04-26T10:48:09.140 に答える
0

解決

これがこの問題の私の解決策です。

オブジェクトを更新する必要があります

@Getter
@Setter
@Document(collection = "projectchild")
public class ProjectChild {
  @Id
  private String _id;

  private String name;

  private String code;

  @Field("desc")
  private String description;

  private String startDate;

  private String endDate;

  @Field("cost")
  private long estimatedCost;

  private List<String> countryList;

  private List<Task> tasks;

  @Version
  private Long version;
}

ソリューションのコーディング

  public Mono<ProjectChild> UpdateCritTemplChild(
       String id, String idch, String ownername) {

    Query query = new Query();
    query.addCriteria(Criteria.where("_id")
                              .is(id)); // find the parent
    query.addCriteria(Criteria.where("tasks._id")
                              .is(idch)); // find the child which will be changed

    Update update = new Update();
    update.set("tasks.$.ownername", ownername); // change the field inside the child that must be updated

    return template
         // findAndModify:
         // Find/modify/get the "new object" from a single operation.
         .findAndModify(
              query, update,
              new FindAndModifyOptions().returnNew(true), ProjectChild.class
                       )
         ;

  }
于 2021-12-23T16:22:34.213 に答える