12

I have this factory collection :

@Document(collection = "factory")
public class Factory
{
    Private List<Product> products;

}

which embeds the Product as products. When I have to add a product to an existing factory :

@Autowired
private FactoryRepository factoryRepository;

public void addProduct(Long id, Product product) {
    Factory f = factoryRepository.findById(id);
    f.addProduct(product);
    factoryRepository.save(f);

}

However, the issue is that product is a large object which contains a set of heavy attributes and the factory can have 2000 products.

So, the retrieved factory causes large memory consumption although it is not required in this phase. Is there a way to append a new product object directly into the factory document without reading the whole object?


EDIT:

As for the comments, I tried :

public void addProduct(Long id, Product product) {
        Document find = new Document("_id",id);
        Document listItem = new Document("products",product);
        Document push = new Document("$push", listItem);
        collection.updateOne(find,push);
}       

This gives error :

org.bson.codecs.configuration.CodecConfigurationException: 
Can't find a codec for class product

So I modified to convert it to a string before push :

public void addProduct(Long id, Product product) {
        Document find = new Document("_id",id);
        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        Document listItem = new Document("products",ow.writeValueAsString(product));
        Document push = new Document("$push", listItem);
        collection.updateOne(find,push);
}     

This pushed object correctly but when reading :

org.springframework.core.convert.ConverterNotFoundException: 
No converter found capable of converting from type [java.lang.String] to type [Product]

Still, I got nowhere here. Any ideas on fixing this issue?

Nilanka Manoj
  • 3,527
  • 4
  • 17
  • 48
  • You could try implementing a distinct update operation for this case. Does that solve your issue? https://stackoverflow.com/questions/15436542/mongodb-java-push-into-array – Benjamin Eckardt Jun 03 '20 at 12:44
  • no, It is very hard to convert this product object directly into a document. Find a solution that maps attributes automatically – Nilanka Manoj Jun 03 '20 at 12:50
  • How many `Factory` objects are in your collection `factory`? Is it only one factory holding all the products? If so, you should have collection `products` instead, where you can insert `Product` objects directly. – bkis Jun 03 '20 at 12:54
  • there can be about 1200 factory objects – Nilanka Manoj Jun 03 '20 at 12:56
  • Aren't you concerned about concurrent updates? If 2 different threads push products to the same factory at the same time there is a chance to lose one of the updates. $push solves both problems. – Alex Blex Jun 03 '20 at 13:47
  • I edited question with trials – Nilanka Manoj Jun 04 '20 at 06:03
  • [Spring Data and MongoDB repository - how to create an update query?](https://stackoverflow.com/questions/24849916/spring-data-and-mongodb-repository-how-to-create-an-update-query) – prasad_ Jun 09 '20 at 06:53

2 Answers2

6

You should use MongoTemplate to update product with push to add to existing products. Something like

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;

@SpringBootApplication
public class So62173077Application {

    public static void main(String[] args) {
        SpringApplication.run(So62173077Application.class, args);
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    @Document(collection = "factory")
    public class Factory
    {
        private Long id;
        private List<Product> products;

    }

    public Long createFactory() {
        Factory factory = new Factory();
        factory.id = 1L;
        return mongoTemplate.insert(factory).id;
    }

    public void addProduct(Long id) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        Update update = new Update();
        Product product = new Product();
        product.name = "stackoverflow";
        update.push("products", product);
        mongoTemplate.updateFirst(query, update, Factory.class);
    }

    private class Product {
        private String name;
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            //Long id = createFactory();
            addProduct(1L);

        };
    }

}
s7vr
  • 73,656
  • 11
  • 106
  • 127
-1

MongoDB large array may be discouraged (fetching, atomic operations, complex querying).

Depending on your needs but I would suggest you to deal with Product in a brand new "product" collection. Your current issue will be solved as well.

Regards.