Is it possible to search for all documents, where a property ends with 'xxx'

150 views
Skip to first unread message

Justin A

unread,
Oct 22, 2011, 1:51:15 AM10/22/11
to ravendb
Hi folks,

Like the subject says -> all documents where the property (say it's
string Name) ends with 'xxxx'

so if i was trying to find all people which end with 'son' .. it's
like Name = '*son'

I think i read that u cannot have a * as the first character, in
lucene. If so, does this I need to reverse the word and then store
that? so if i'm going to search for *son .. then i would search on
'nos*' instead?

Shiferaw Behailu

unread,
Oct 22, 2011, 3:33:18 AM10/22/11
to rav...@googlegroups.com
Hi,
 
It worked for me. I had a property 'FullName' and want to populate, say all Employees with full name having b in it, i used *b* and it is working. I didn't have a reduce though.

Justin A

unread,
Oct 22, 2011, 3:52:23 AM10/22/11
to ravendb
So lucene used in RavenDb can handle the * at the start of the search
argument?

Dody Gunawinata

unread,
Oct 22, 2011, 6:35:14 AM10/22/11
to rav...@googlegroups.com
http://lucene.apache.org/java/2_4_0/queryparsersyntax.html

On Sat, Oct 22, 2011 at 9:52 AM, Justin A <jus...@adler.com.au> wrote:
> So lucene used in RavenDb can handle the * at the start of the search
> argument?
>

--
nomadlife.org

Justin A

unread,
Oct 22, 2011, 8:20:45 AM10/22/11
to ravendb
Yeah .. and check this out..

quote:

Note: You cannot use a * or ? symbol as the first character of a
search.

ZNS

unread,
Oct 22, 2011, 8:58:48 AM10/22/11
to ravendb

I actually think it's possible to add a wildcard to the beginning of a
term, however the performance is affected. Check out this interesting
thread on the subject: http://www.gossamer-threads.com/lists/lucene/java-user/13373

Ayende Rahien

unread,
Oct 22, 2011, 11:42:19 AM10/22/11
to rav...@googlegroups.com
Justin,
EndsWith queries are supported, but are expensive.
You are better of doing a reverse StartsWith.

from doc in docs
select new { ReverseName = new String(doc.Name.Reverse()) }

Justin A

unread,
Oct 23, 2011, 8:56:18 AM10/23/11
to ravendb
Hi Ayende,

hmm.. embarassing :( I totally screwed this up.

So first I tried to make a simple Map index..

public class LogEntries_ByClientIdReversed :
AbstractIndexCreationTask<LogEntry>
{
public LogEntries_ByClientIdReversed()
{
Map = docs => from doc in docs
select new {ReverseName = new
string(doc.ClientGuid.Reverse().ToArray())};
}
}

and that compiles.

but from there, I wasn't sure how to use it .. considering I'm
creating a new Property called 'ReverseName'.

I start thinking I needed something like ..

var clients = documentSession.Query<result,
LogEntries_ByClientIdReversed>()... but that means I need a map/
reduce .. which i didn't think I needed here.

can you lead me back on to the right path, please?

Dody Gunawinata

unread,
Oct 23, 2011, 9:32:40 AM10/23/11
to rav...@googlegroups.com
var clients = session.Advanced.LuceneQuery<result,
LogEntries_ByClientIdReversed>()
.Search("ReverseName", "xxx*")
.Select ( x =>
new {
yyy = x.Prop,
yy = x.Prop2
});

--
nomadlife.org

Ayende Rahien

unread,
Oct 23, 2011, 3:56:25 PM10/23/11
to rav...@googlegroups.com
You can do it like this:

public class LogEntries_ByClientIdReversed : AbstractIndexCreationTask<LogEntry, LogEntries_ByClientIdReversed.Result>
{
  public class Result
  {
    public string ReverseName {get;set;}
  }

   public LogEntries_ByClientIdReversed()
   {
       Map = docs => from doc in docs
                       select new {ReverseName = new string(doc.ClientGuid.Reverse().ToArray())};
   }
}

You don't need a reduce phase to be able to query on that properly

On Sun, Oct 23, 2011 at 2:56 PM, Justin A <jus...@adler.com.au> wrote:

Justin A

unread,
Nov 5, 2011, 10:47:36 PM11/5/11
to ravendb
Hi Ayende.

Sorry to cast 'raise-undead' on a sort of oldish post .. but I thought
I had this working.

Anways, when I was unit testing this last night/yesterday, I was
getting no results. After a while, I noticed that the INDEX has an
error

Screenshot: http://imgur.com/B7Aj2
Message: 'string' does not contain a definition for 'Reverse'

So, I quickly made a repo project. Code is all here: http://pastie.org/2818282
Just copy/paste that into a new project / class.

Of course, when I use the embeddable DS, I don't get any info about
the index having errors.
It's only when i swap that over to a normal DS and then use my fav
browser to goto /stats do i see the errors.

eg.
{
Index: "Users/ByDisplayNameReversed"
Error: "'string' does not contain a definition for 'Reverse'"
Timestamp: "2011-11-06T02:40:38.4293484Z"
Document: "users/1"
}

Any suggestions? I tried various permutations of trying to create a
string .. but everything always comes back to using the .Reverse()
string extension method.
Could it be related to the fact that .Reverse() is a string extension
method?

-J-

Oren Eini (Ayende Rahien)

unread,
Nov 6, 2011, 6:11:45 AM11/6/11
to rav...@googlegroups.com
Try doing:

new string(Enumerable.Reverse(str));

Instead?

Justin A

unread,
Nov 6, 2011, 6:45:34 AM11/6/11
to ravendb
Nope. First, it doesn't complile. String/string ctors don't accept an
IEnumerable<char>. So then i though i might just manually update the
index definition in Raven Studio.
Error there, after the mod, is: The best overload method match for
string.String(char *)' has some invalid arguments.

So then I added the .ToArray at the end.
eg.

public Users_ByDisplayNameReversed()
{
Map = docs => from doc in docs
select new
{
doc.Id,
doc.DisplayName,
DisplayNameReversed = new
string(Enumerable.Reverse(doc.DisplayName).ToArray()),
};
}


and that errors saying: 'object' does not contain a definition for
'ToArray()'. That does compile, though. Error is listed in the /stats
or in raven studio, under the specific index, info page.

Justin A

unread,
Nov 7, 2011, 1:48:52 AM11/7/11
to ravendb
Ayende or Itamar, is there a passing test i can check out, in RavenDb
on github?

Itamar Syn-Hershko

unread,
Nov 7, 2011, 2:47:23 AM11/7/11
to rav...@googlegroups.com
Can you try  new string(Enumerable.Reverse(doc.DisplayName).ToString().ToArray()) 

Itamar Syn-Hershko

unread,
Nov 7, 2011, 2:50:21 AM11/7/11
to rav...@googlegroups.com
If that doesn't work, please post a failing test and we'll go from there

Justin A

unread,
Nov 7, 2011, 9:56:28 PM11/7/11
to ravendb
Hi Itamar,

I know i'm not really good at this, so please forgive my noobness.

I forked ayende/ravendb .. and added the unit test repo.

I then made a pull request (https://github.com/ayende/ravendb/pull/71)

With my repo, when i try and do a query on the index, I get back zero
results. I'm assuming this is because the Index is erroring. I
certainly hope my pull request is satisfactory as a repo.

If the code is acceptable, I'll sign/email one of those contributing
pdf's if the code will be accepted, etc.

I take it, this is all that is required from you now?

-J-

Matt Warren

unread,
Nov 8, 2011, 9:28:22 AM11/8/11
to ravendb
Change your Map index to this, it's not the prettiest, but it'll
work:     new IndexDefinition       {              Map =   "from doc
in docs.Users select new { doc.Name, ReverseName = String.Join(\"\",
Enumerable.Reverse(doc.Name.ToString())) }"        });
I've put up a full code sample that also checks that ReverseNames can
be queried using WhereStartsWith("ReverseName", "e"). See http://pastie.org/2830979
U guess it would be nicer if there was an extension method that
RavenDB indexes can access, for instance:
  static string Reverse(this string input)  {      char[] arr =
input.ToCharArray();      Array.Reverse(arr);      return new
string(arr);  }

Matt Warren

unread,
Nov 8, 2011, 9:30:32 AM11/8/11
to ravendb
This time with formatting!!!

Change your Map index to this, it's not the prettiest, but it'll work:
     new IndexDefinition
       {
              Map =   "from doc in docs.Users select new { doc.Name,
ReverseName = String.Join(\"\",
Enumerable.Reverse(doc.Name.ToString())) }"
        });

I've put up a full code sample that also checks that ReverseNames can
be queried using WhereStartsWith("ReverseName", "e"). See http://pastie.org/2830979

I guess it would be nicer if there was an extension method that
RavenDB indexes can access, for instance:

  static string Reverse(this string input)
  {
      char[] arr = input.ToCharArray();
      Array.Reverse(arr);
      return new string(arr);
  }

On Nov 8, 2:28 pm, Matt Warren <mattd...@gmail.com> wrote:
> Change your Map index to this, it's not the prettiest, but it'll
> work:     new IndexDefinition       {              Map =   "from doc
> in docs.Users select new { doc.Name, ReverseName = String.Join(\"\",
> Enumerable.Reverse(doc.Name.ToString())) }"        });
> I've put up a full code sample that also checks that ReverseNames can
> be queried using WhereStartsWith("ReverseName", "e"). Seehttp://pastie.org/2830979

Oren Eini (Ayende Rahien)

unread,
Nov 9, 2011, 5:38:02 PM11/9/11
to rav...@googlegroups.com
Thanks for the failing test case, now I know what is wrong. It is an issue of extension method lookup in a dynamic context, that is all.
Looking to see what will work now.

Justin A

unread,
Nov 9, 2011, 6:00:22 PM11/9/11
to ravendb
Ayende - this is a bit offtopic .. but the unit test and pull
request .. is that the way you guys would like repo's, in?

(ie. did I do it right?)

I've never done it before .. so I'm just making sure I've understood
it right.

Oren Eini (Ayende Rahien)

unread,
Nov 9, 2011, 6:01:22 PM11/9/11
to rav...@googlegroups.com
That was _wonderful_, exactly the right way to do that, yes.

Issue is fixed in the next build, by the way. 
Here is how you use it:

public class ReversedResult
{
public string ReverseName { get; set; }
}
[Fact]
public void CanQueryInReverse()
{
using (EmbeddableDocumentStore store = NewDocumentStore())
{
store.DatabaseCommands.PutIndex("StringReverseIndex",
new IndexDefinition
{
Map =
"from doc in docs select new { doc.Name, ReverseName = doc.Name.Reverse())}"
});

using (IDocumentSession documentSession = store.OpenSession())
{
documentSession.Store(new User { Name = "Ayende" });
documentSession.Store(new User { Name = "Itamar" });
documentSession.Store(new User { Name = "Pure Krome" });
documentSession.Store(new User { Name = "John Skeet" });
documentSession.Store(new User { Name = "StackOverflow" });
documentSession.Store(new User { Name = "Wow" });
documentSession.SaveChanges();
}

using (IDocumentSession documentSession = store.OpenSession())
{
var users = documentSession
.Query<ReversedResult>("StringReverseIndex")
.Customize(x => x.WaitForNonStaleResults())
.Where(x=>x.ReverseName.StartsWith("edn"))
.As<User>()
.ToList();

Assert.Empty(store.DocumentDatabase.Statistics.Errors);
Assert.True(users.Count > 0);

Justin A

unread,
Nov 9, 2011, 6:44:45 PM11/9/11
to ravendb
*beams* :) awesome :) First time is always .. scary.

>>It is an issue of extension method lookup in a dynamic context, that is all.

Once the issue was confirmed as repo'd .. I was going to ask what the
core issue was. I like to know what something did wrong .. not just
get an answer and moving on.

Kewl!

cheers, team!

Justin A

unread,
Nov 10, 2011, 4:12:51 AM11/10/11
to ravendb
Will this (bugfix) be available in the next NuGet anytime soon?

Matt Warren

unread,
Nov 10, 2011, 4:38:34 AM11/10/11
to ravendb
Nice, I guess this means that other extension methods can now be
easily made available inside indexes, for instance ToLower(), etc?

On Nov 9, 11:01 pm, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
wrote:

Itamar Syn-Hershko

unread,
Nov 10, 2011, 5:08:22 AM11/10/11
to rav...@googlegroups.com
Yes, in a week or two

Itamar Syn-Hershko

unread,
Nov 10, 2011, 5:10:35 AM11/10/11
to rav...@googlegroups.com
It requires making changes to the server side

Oren Eini (Ayende Rahien)

unread,
Nov 10, 2011, 8:29:48 AM11/10/11
to rav...@googlegroups.com
Yes... but we need to expose that on our end, not just exposing all methods like that.

Oren Eini (Ayende Rahien)

unread,
Nov 10, 2011, 8:29:58 AM11/10/11
to rav...@googlegroups.com
Mind, ToLower is already there, it is an instance method

Justin A

unread,
Feb 21, 2012, 8:42:02 AM2/21/12
to rav...@googlegroups.com
Heya Ayende.

i know this is an oldish thread, but I've finally had a chance to get around to giving this a go :) It works great .. but sometimes i was still getting the following errors (this is a debug write line of a random DocStore errors)...


Document: logentries/7; Index: LogEntries/Search; Error: The call is ambiguous between the following methods or properties: 'Raven.Database.Linq.PrivateExtensions.DynamicExtensionMethods.Reverse(string)' and 'Raven.Database.Linq.PrivateExtensions.DynamicExtensionMethods.Reverse(System.Collections.Generic.IEnumerable<object>)'

wtf?

checking that item .. well .. the property to be reversed was NULL!

So .. this now works .. but can you guys confirm if this is OK to do? (or is there a proper way to do this)

Map = "from doc in docs select new { doc.Name, ReverseName = string.IsNullOrEmpty(doc.Name) ? null : doc.Name.Reverse())}"

so it doesn't try and reverse a null property in the doc collection.



Itamar Syn-Hershko

unread,
Feb 21, 2012, 12:29:45 PM2/21/12
to rav...@googlegroups.com
Looks legit, yes, but the null reference thing shouldn't have happened I think.

Oren Eini (Ayende Rahien)

unread,
Feb 21, 2012, 4:39:12 PM2/21/12
to rav...@googlegroups.com
Justin,
Yeah, that is probably a bug. Can you open an issue for that?

Justin A

unread,
Feb 21, 2012, 7:52:18 PM2/21/12
to rav...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages