How to write catalog with many categories(taxonomy >>100)?

31 views
Skip to first unread message

Valeriy Solovyov

unread,
Feb 25, 2015, 3:05:27 AM2/25/15
to mgo-...@googlegroups.com

I am newbie to golang, who moved from dynamic typed language.

I faced with problem how to write catalog with many categories/subcategory — complex taxonomy (there are 100+ types)

For example: Shoestring > Shoes > Men > shoes > clothes > Home > Categories

I am using mongodb as backend. I can't understand how to write CRUD operation's in this case?

If I will process all queries as usual:

func RunFindAllQuery(document interface{}, m bson.M, mongoSession *mgo.Session, conn Conn) (err error) {
sessionCopy
:= mongoSession.Copy()
defer sessionCopy
.Close()
collection
:= sessionCopy.DB(conn.Database).C(conn.Collection)  
err
= collection.Find(m).All(document)
if err != nil {
    log
.Printf("RunQuery : ERROR : %s\n", err)
}
   
return err
}

I will need to define many types, because shoes != car:

type Shoes struct {
Id          bson.ObjectId `bson:"_id"`
Name         string        `bson:"name"`
Description string        `bson:"descriprion"`
Size        float    `bson:"size"`
Color        float    `bson:"color"` 
Type        float    `bson:"type"` 
ShoeHeight        float    `bson:"shoeheight"` 
PlatformHeight        float    `bson:"platformheight"` 
Country        float    `bson:"country"` 
}
type Car struct {
Id          bson.ObjectId `bson:"_id"`
Name         string        `bson:"name"`
Model         CarModel        `bson:"name"`
Description string        `bson:"descriprion"`
Color        float    `bson:"color"` 
Height        float    `bson:"height"` 
Fueltype string    `bson:"fueltype"` 
Country        float    `bson:"country"` 
}

And my code will be copypaste:

var carobjFindAll []Car
m := bson.M{"description": "description"}
_ = RunFindAllQuery(&carobjFindAll, m, mongoSession, conn)
for cur := range carobjFindAll {
    fmt.Printf("\nId: %s\n", carobjFindAll[cur].Id)
    fmt.Printf("\nColor: %s\n", carobjFindAll[cur].Color)
}
var shoesobjFindAll []Shoes
m_shoes := bson.M{"description": "shoes_description"}
_ = RunFindAllQuery(&shoesobjFindAll, m_shoes, mongoSession, conn)
for cur_shoe := range shoesobjFindAll {
    fmt.Printf("\nId: %s\n", shoesobjFindAll[cur_shoe].Id)
    fmt.Printf("\nColor: %s\n", shoesobjFindAll[cur_shoe].Color)
}


Maybe there is another way how to receive/update data in mongodb without writing thousand line of copypaste?




Gustavo Niemeyer

unread,
Feb 25, 2015, 9:07:56 AM2/25/15
to mgo-...@googlegroups.com
> Maybe there is another way how to receive/update data in mongodb without writing thousand line of copypaste?

It's not entirely clear the exact portion you're struggling with,
because your example uses common code to receive data from MongoDB,
and it does not update it, so there's no copy & paste related to
MongoDB receive/update at all there.

What you seem to be struggling with (although I might be wrong here)
is that you have different types to handle, and that's as much an
issue in a statically compiled language as it is in a dynamic one. It
does not make sense to access "shoeHeight" in a "Car" type in a
dynamic language either, for example.

So, what is the exact portion you are finding it difficult to adapt to
in the new context?
> --
> You received this message because you are subscribed to the Google Groups
> "mgo-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to mgo-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--

gustavo @ http://niemeyer.net

Norberto Leite

unread,
Feb 25, 2015, 10:55:47 AM2/25/15
to mgo-...@googlegroups.com
Hi Valeriy, 

adding up some more content around Gustavo's remarks, this is not an issue around a language itself. It's more a schema design challenge that you are going to face no matter the language you use. 

Apart from this MongoDB is usually a good fit for this use cases and I can guarantee that there are lot's of people implementing this on strongly type languages.  
I recommend you having a look (if you haven't already) to this presentation.

N.


  "name"     : "Norberto Leite",

  "title"    : "Technical Evangelist",

  "phone"    : "0034.687.954.987",

  "location" : ["Madrid, ES"],

  "twitter"  : ["@nleite"],

}


MongoDB World is back!  June 1-2 in New York. Sponsorships and Call for Proposals now open!

Valeriy Solovyov

unread,
Feb 26, 2015, 7:37:20 AM2/26/15
to mgo-...@googlegroups.com
Thank you Gustavo and  Norberto,

It's not entirely clear the exact portion you're struggling with,
> because your example uses common code to receive data from MongoDB,
and it does not update it, so there's no copy & paste related to
MongoDB receive/update at all there.
I didn't wrote because I thought, that it is unnecessary.

>  you have different types to handle, and that's as much an
issue in a statically compiled language as it is in a dynamic one. It
does not make sense to access "shoeHeight" in a "Car" type in a
dynamic language either, for example.
Ok. 
I had php code:

<?php
$m 
= new MongoClient();
$db $m->selectDB('test');
$collection = new MongoCollection($db'produce');
// search for fruits
$fruitQuery = array('Type' => 'Fruit');
$fruitArray $collection->find($fruitQuery);
$fruitArray;
$template $twig->loadTemplate('fruits.tmpl');
echo $template->render(array('fruitArray' => $fruitArray,))
?>
Twig template fruits.tmpl:
<div class="fruits">
{% for fruit in fruitArray %}
	{% if fruit.name|length %}
		<div>{{ fruit.name }}</div>
	{% endif %}	
	{% if fruit.description|length %}
		<div>description: {{ fruit.description }}</div>
	{% endif %}	
{% endfor %}
</div>


>this is not an issue around a language itself
In php you can receive array of results/ query array of param's and you are using one code for this.
Parsing of params will be in one place and once -> template.

Now what I see in go with code above:
1.create special type
2.write special functions for processing all result objects after querying to MongoDB. 
   In my code it's 
func ProcessCarsFind(document interface{}, m bson.M, mongoSession *mgo.Session, conn Conn) (err error) {
var shoesobjFindAll []Shoes
m_shoes := bson.M{"description": "shoes_description"}
_ = RunFindAllQuery(&shoesobjFindAll, m_shoes, mongoSession, conn)
for cur_shoe := range shoesobjFindAll {

    fmt.Printf("\n<div>Id: %s</div>\n", shoesobjFindAll[cur_shoe].Id)    } }
3. validation code will be copy-paste

Regards.


--
You received this message because you are subscribed to a topic in the Google Groups "mgo-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mgo-users/LfFIIjvtjBo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mgo-users+...@googlegroups.com.

Gustavo Niemeyer

unread,
Feb 26, 2015, 9:01:31 AM2/26/15
to mgo-...@googlegroups.com
On Thu, Feb 26, 2015 at 9:37 AM, Valeriy Solovyov <weldp...@gmail.com> wrote:
Now what I see in go with code above:
1.create special type
2.write special functions for processing all result objects after querying to MongoDB.

I do understand that you are struggling a bit coming from PHP into a statically typed language, but you really don't need to do any copy & paste in your code that you did not already have to do. Go and the mgo driver allow you to use dynamic types (maps, bson.D, etc) and reflecttion whenever you want to. That does not mean it is a good idea at all times, though. Static typing is a superb partner to MongoDB's total structure flexibility.

One great way to understand how to properly organize code is to read how experienced developers do it. Even Go's standard library will give you some insight into that problem. Try to find areas that you imagined would require massive copy & paste as you suggest, and then figure how they avoided that.

Klaus Post

unread,
Feb 26, 2015, 11:53:13 AM2/26/15
to mgo-...@googlegroups.com
Hi Valeriy

To elaborate a bit on Gustavos excellent suggestions, you can do a lot of the things you seem to require by using embedded structs. I have made a small example on how that could be structured for you here:


This shows how to share common properties easily.


However, the main issue you seem to have is that you are requesting different types. Using a map[string]interface{} is definitely possible, but you can do something better, if you override the BSON Unmarshal function.

Here is an example (it requires bson, so it doesn't run on the playground)


Using this method you can decode many different types into a type safe struct, and later get the typed object based on that "type" is set to in the object.

The basic of it is: When we get an object, we first decode into a struct that only catches the "Type" field. Once we have that, we create the "correct" object to decode into, and run the decoding once more, but this time catching all fields.

Instead of switching on types, you could also check the type for interfaces, so you can run a "Validate" function on all objects that support this, without code duplication. 

You can of course extend this to have type registration and use reflection to get the proper object type, but that may be beyond the scope of your project.

I hope this helps you.


/Klaus

PS. You might also find the struct generator at http://www.jsonstruct.com/ helpful. With this you can dump sample json output into the generator, and it will create stucts for you. Modifying it for bson is trivial.


Valeriy Solovyov

unread,
Mar 5, 2015, 10:04:46 AM3/5/15
to mgo-...@googlegroups.com
Thank you for advice's and examples.


четверг, 26 февраля 2015 г., 18:53:13 UTC+2 пользователь Klaus Post написал:
Reply all
Reply to author
Forward
0 new messages