#ForeignCollectionField with multiple orderColumnName? - ormlite

I am using ORMlite to henbdle database operation for Android app.
I have foreign collection which I would like to order by using two columns instead of one:
Instead of current:
#ForeignCollectionField(eager = false, orderColumnName = "name")
private Collection<HistoricalFigureLink> historicalFigureLinks;
Something like this:
#ForeignCollectionField(eager = false, orderColumnName = "type,name")
private Collection<HistoricalFigureLink> historicalFigureLinks;
I have two workarounds - sort resulting collection manually with custom comparator after retrieving it or to retrieve collection with separate query - but I would like to avoid this kind of clutter.

Related

Changing a value in a listfield of embedded documents on MongoEngine

I am learning how to use MongoEngine and MongoDB, and I know how to query over Listfield(EmbeddedDocumentField) from this question:
can't query over ListField(EmbeddedDocumentField)
Sort of nasty. Kind of wish there was something easier than that.
I know how to change the name of an agent using the same example from the link:
Agent.objects(name="Brenna Li").update_one(set__name="Brenna Smith")
But how can I change a value inside an embedded document in a listfield? For example, what is the code I need to change Brenna Li's skill level in C++ from a 6 to a 8 and her skill level in Java from 4 to a 5?
You can use the positional operator $ or S in mongoengine (so it can be used as a keyword argument). However, you can only update a single match at a time. Making it impossible to update both the Java and C++ levels in a single query - without replacing the whole Skills list (which wouldnt be very safe).
To do it in two queries you could do something like:
class Skill(EmbeddedDocument):
name = StringField(required = True)
level = IntField(required = True)
class Agent(Document):
name = StringField(required = True)
email = EmailField(required = True, unique = True)
skills = ListField(EmbeddedDocumentField(Skill))
Agent.drop_collection()
Agent(name="Brenna Li", email="br#example.com",
skills=[Skill(name="Java", level=2),
Skill(name="Surfing", level=6),
Skill(name="c++", level=4)]).save()
Agent.objects.filter(name="Brenna Li", skills__name="Java").update(set__name="Brenna Smith", inc__skills__S__level=1)
Agent.objects.filter(name="Brenna Smith", skills__name="c++").update(inc__skills__S__level=1)

CouchDB Map/Reduce view query from Ektorp

I'm trying to execute a query from java against a Map/Reduce view I have created on the CouchDB.
My map function looks like the following:
function(doc) {
if(doc.type == 'SPECIFIC_DOC_TYPE_NAME' && doc.userID){
for(var g in doc.groupList){
emit([doc.userID,doc.groupList[g].name],1);
}
}
}
and Reduce function:
function (key, values, rereduce) {
return sum(values);
}
The view seems to be working when executed from the Futon interface (without keys specified though).
What I'm trying to do is to count number of some doc types belonging to a single group. I want to query that view using 'userID' and name of the group as a keys.
I'm using Ektorp library for managing CouchDB data, if I execute this query without keys it returns the scalar value, otherwise it just prints an error saying that for reduce query group=true must be specified.
I have tried the following:
ViewQuery query = createQuery("some_doc_name");
List<String> keys = new ArrayList<String>();
keys.add(grupaName);
keys.add(uzytkownikID);
query.group(true);
query.groupLevel(2);
query.dbPath(db.path());
query.designDocId(stdDesignDocumentId);
query.keys(keys);
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
above example works without 'keys' specified.
I have other queries working with ComplexKey like eg:
ComplexKey key = ComplexKey.of(userID);
return queryView("list_by_userID",key);
but this returns only a list of type T (List) - using CouchDbRepositorySupport of course - and cannot be used with reduce type queries (from what I know).
Is there any way to execute the query with reduce function specified and a complex key with 2 or more values using Ektorp library? Any examples highly appreciated.
Ok, I've found the solution using trial and error approach:
public int getNumberOfDocsAssigned(String userID, String groupName) {
ViewQuery query = createQuery("list_by_userID")
.group(true)
.dbPath(db.path())
.designDocId(stdDesignDocumentId)
.key(new String[]{userID,groupName});
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
}
So, the point is to send the complex key (not keys) actually as a single (but complex) key containing the String array, for some reason method '.keys(...)' didn't work for me (it takes a Collection as an argument). (for explanation on difference between .key() and .keys() see Hendy's answer)
This method counts all documents assigned to the specific user (specified by 'userID') and specific group (specified by 'groupName').
Hope that helps anybody executing map/reduce queries for retrieving scalar values from CouchDB using Ektorp query.
Addition to Kris's answer:
Note that ViewQuery.keys() is used when you want to query for documents matching a set of keys, not for finding document(s) with a complex key.
Like Kris's answer, the following samples will get document(s) matching the specified key (not "keys")
viewQuery.key("hello"); // simple key
viewQuery.key(documentSlug); // simple key
viewQuery.key(new String[] { userID, groupName }); // complex key, using array
viewQuery.key(ComplexKey.of(userID, groupName)); // complex key, using ComplexKey
The following samples, on the other hand, will get document(s) matching the specified keys, where each key may be either a simple key or a complex key:
// simple key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of("hello"));
viewQuery.keys(ImmutableSet.of(documentSlug1));
// simple keys
viewQuery.keys(ImmutableSet.of("hello", "world"));
viewQuery.keys(ImmutableSet.of(documentSlug1, documentSlug2));
// complex key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 } ));
// complex keys
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" },
new String[] { "Mary", "Jane" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 },
new String[] { userID2, groupName2 } ));
// a simple key and a complex key. while technically possible,
// I don't think anybody actually does this
viewQuery.keys(ImmutableSet.of(
"hello",
new String[] { "Mary", "Jane" } ));
Note: ImmutableSet.of() is from guava library.
new Object[] { ... } seems to have same behavior as ComplexKey.of( ... )
Also, there are startKey() and endKey() for querying using partial key.
To send an empty object {}, use ComplexKey.emptyObject(). (only useful for partial key querying)

Merge two collections in mongodb using c#

I have two collections in mongodb.I am retreiving data from two collections independently working gud.But when I am implementing paging using skip and take methods I am getting data from both the collections like this
paging = new Pagination() { CurrentPage = pageNumber, ItemsPerPage = 16 };
var results = dataTable.FindAs<TradeInfo(queryAll).Skip(paging.Skip).Take(paging.Take).ToList<TradeInfo>();
paging.TotalCount = Convert.ToInt32(dataTable.Find(query).Count());
var results2 = new List<TradeInfo>();
if (dataTable2 != null)
{
results2 = dataTable2.FindAs<TradeInfo(queryAll).Skip(paging.Skip).Take(paging.Take).ToList<TradeInfo>();
int count = Convert.ToInt32(dataTable2.Find(query).Count());
paging.TotalCount = paging.TotalCount + count;
results.AddRange(results2);
}
I am giving results as Itemssource to Datagrid and I am getting total 32 items per page.
How can I do that is there any joins concept in Mongodb.Two collections columns are same.
How can I do it?
Please help me in doing that....
Thanks,
jan
I believe what you are looking for here is more of a Union than a Join.
Unfortunately there is no such concept in MongoDB. If your paging is dependent on a query, which in this case it seems it might be, your only real option is to create and maintain a single merged collection which gets updated every time a document is added or saved to either of these two collections. Then you can skip and take on the single collection after applying the query to it.

How do I get the date a MongoDB collection was created using MongoDB C# driver?

I need to iterate through all of the collections in my MongoDB database and get the time when each of the collections was created (I understand that I could get the timestamp of each object in the collection, but I would rather not go that route if a simpler/faster method exists).
This should give you an idea of what I'm trying to do:
MongoDatabase _database;
// code elided
var result = _database.GetAllCollectionNames().Select(collectionName =>
{
_database.GetCollection( collectionName ) //.{GetCreatedDate())
});
As far as I know, MongoDB doesn't keep track of collection creation dates. However, it's really easy to do this yourself. Add a simple method, something like this, and use it whenever you create a new collection:
public static void CreateCollectionWithMetadata(string collectionName)
{
var result = _db.CreateCollection(collectionName);
if (result.Ok)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
collectionMetadata.Insert(new { Id = collectionName, Created = DateTime.Now });
}
}
Then whenever you need the information just query the collectionMetadata collection. Or, if you want to use an extension method like in your example, do something like this:
public static DateTime GetCreatedDate(this MongoCollection collection)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
var metadata = collectionMetadata.FindOneById(collection.Name);
var created = metadata["Created"].AsDateTime;
return created;
}
The "creation date" is not part of the collection's metadata. A collection does not "know" when it was created. Some indexes have an ObjectId() which implies a timestamp, but this is not consistent and not reliable.
Therefore, I don't believe this can be done.
Like Mr. Gates VP say, there is no way using the metadata... but you can get the oldest document in the collection and get it from the _id.
Moreover, you can insert an "empty" document in the collection for that purpose without recurring to maintain another collection.
And it's very easy get the oldest document:
old = db.collection.find({}, {_id}).sort({_id: 1}).limit(1)
dat = old._id.getTimestamp()
By default, all collection has an index over _id field, making the find efficient.
(I using MongoDb 3.6)
Seems like it's some necroposting but anyway: I tried to find an answer and got it:
Checked it in Mongo shell, don't know how to use in C#:
// db.payload_metadata.find().limit(1)
ObjectId("60379be2bec7a3c17e6b662b").getTimestamp()
ISODate("2021-02-25T12:45:22Z")

MongoDB C# offic. List<BsonObject> query issue and always olds values?

I have not clearly issue during query using two criterials like Id and Other. I use a Repository storing some data like id,iso,value. I have created an index("_id","Iso") to performs queries but queries are only returning my cursor if i use only one criterial like _id, but is returning nothing if a use two (_id, Iso) (commented code).
Are the index affecting the response or the query method are failing?
use :v1.6.5 and C# official.
Sample.
//Getting Data
public List<BsonObject> Get_object(string ID, string Iso)
{
using (var helper = BsonHelper.Create())
{
//helper.Db.Repository.EnsureIndex("_Id","Iso");
var query = Query.EQ("_Id", ID);
//if (!String.IsNullOrEmpty(Iso))
// query = Query.And(query, Query.EQ("Iso", Iso));
var cursor = helper.Db.Repository.FindAs<BsonObject>(query);
return cursor.ToList();
}
}
Data:
{
"_id": "2345019",
"Iso": "UK",
"Data": "Some data"
}
After that I have Updated my data using Update.Set() methods. I can see the changed data using MongoView. The new data are correct but the query is always returning the sames olds values. To see these values i use a page that can eventually cached, but if add a timestamp at end are not changing anything, page is always returning the same olds data. Your comments are welcome, thanks.
I do not recall offhand how the C# driver creates indexes, but the shell command for creating an index is like this:
db.things.ensureIndex({j:1});
Notice the '1' which is like saying 'true'.
In your code, you have:
helper.Db.Repository.EnsureIndex("_Id","Iso");
Perhaps it should be:
helper.Db.Repository.EnsureIndex("_Id", 1);
helper.Db.Repository.EnsureIndex("Iso", 1);
It could also be related to the fact that you are creating indexes on "_Id" and the actual id field is called "_id" ... MongoDB is case sensitive.
Have a quick look through the index documentation: http://www.mongodb.org/display/DOCS/Indexes